diff --git a/.gitignore b/.gitignore index b772f2141..84e2a081d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ website/node_modules terraform-provider-genesyscloud .env .vscode - +**/true website/vendor vendor/ diff --git a/docs/resources/routing_email_route.md b/docs/resources/routing_email_route.md index a89a4e886..3ed8f126a 100644 --- a/docs/resources/routing_email_route.md +++ b/docs/resources/routing_email_route.md @@ -48,18 +48,18 @@ resource "genesyscloud_routing_email_route" "support-route" { ### Required - `domain_id` (String) ID of the routing domain such as: 'example.com'. Changing the domain_id attribute will cause the email_route object to be dropped and recreated with a new ID. -- `from_email` (String) The sender email to use for outgoing replies. - `from_name` (String) The sender name to use for outgoing replies. - `pattern` (String) The search pattern that the mailbox name should match. ### Optional -- `auto_bcc` (Block Set) The recipients that should be automatically blind copied on outbound emails associated with this route. (see [below for nested schema](#nestedblock--auto_bcc)) +- `auto_bcc` (Block Set) The recipients that should be automatically blind copied on outbound emails associated with this route. This should not be set if reply_email_address is specified. (see [below for nested schema](#nestedblock--auto_bcc)) - `flow_id` (String) The flow to use for processing the email. This should not be set if a queue_id is specified. +- `from_email` (String) The sender email to use for outgoing replies. This should not be set if reply_email_address is specified. - `language_id` (String) The language to use for routing. - `priority` (Number) The priority to use for routing. - `queue_id` (String) The queue to route the emails to. This should not be set if a flow_id is specified. -- `reply_email_address` (Block List, Max: 1) The route to use for email replies. (see [below for nested schema](#nestedblock--reply_email_address)) +- `reply_email_address` (Block List, Max: 1) The route to use for email replies. This should not be set if from_email or auto_bcc are specified. (see [below for nested schema](#nestedblock--reply_email_address)) - `skill_ids` (Set of String) The skills to use for routing. - `spam_flow_id` (String) The flow to use for processing inbound emails that have been marked as spam. @@ -89,6 +89,6 @@ Required: Optional: - `route_id` (String) ID of the route. -- `self_reference_route` (Boolean) Use this route as the reply email address. If true you will use the route id for this resource as the reply and you +- `self_reference_route` (Boolean) Use this route as the reply email address. If true you will use the route id for this resource as the reply and you can not set a route. If you set this value to false (or leave the attribute off)you must set a route id. Defaults to `false`. diff --git a/genesyscloud/resource_exporter/resource_exporter.go b/genesyscloud/resource_exporter/resource_exporter.go index 23a2e99a6..e5d71450b 100644 --- a/genesyscloud/resource_exporter/resource_exporter.go +++ b/genesyscloud/resource_exporter/resource_exporter.go @@ -2,15 +2,17 @@ package resource_exporter import ( "context" - "github.com/hashicorp/go-cty/cty" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "regexp" "strings" "sync" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + lists "terraform-provider-genesyscloud/genesyscloud/util/lists" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) var resourceExporters map[string]*ResourceExporter @@ -51,7 +53,7 @@ type ResourceInfo struct { // Allows the definition of a custom resolver for an exporter. type RefAttrCustomResolver struct { - ResolverFunc func(map[string]interface{}, map[string]*ResourceExporter) error + ResolverFunc func(map[string]interface{}, map[string]*ResourceExporter, string) error } // Allows the definition of a custom resolver for an exporter. diff --git a/genesyscloud/resource_exporter/resource_exporter_custom.go b/genesyscloud/resource_exporter/resource_exporter_custom.go index 689666b56..682ffd70c 100644 --- a/genesyscloud/resource_exporter/resource_exporter_custom.go +++ b/genesyscloud/resource_exporter/resource_exporter_custom.go @@ -15,7 +15,7 @@ This causes problems with the exporter because our export process expects id to This customer custom router will look at the member_group_type and resolve whether it is SKILLGROUP, GROUP type. It will then find the appropriate resource out of the exporters and build a reference appropriately. */ -func MemberGroupsResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter) error { +func MemberGroupsResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter, resourceName string) error { memberGroupType := configMap["member_group_type"] memberGroupID := configMap["member_group_id"].(string) @@ -51,7 +51,7 @@ by the export process. Example: properties = {"contact.Attempts" = ""}. During the export process the value associated with the key is set to nil. This custom exporter checks if a key has a value of nil and if it does sets it to an empty string so it is exported. */ -func RuleSetPropertyResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter) error { +func RuleSetPropertyResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter, resourceName string) error { if properties, ok := configMap["properties"].(map[string]interface{}); ok { for key, value := range properties { if value == nil { @@ -70,7 +70,7 @@ and we have an array of attributes wrapped in a string. This customer custom router will look at the skills array if present and resolve each string id find the appropriate resource out of the exporters and build a reference appropriately. */ -func RuleSetSkillPropertyResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter) error { +func RuleSetSkillPropertyResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter, resourceName string) error { if exporter, ok := exporters["genesyscloud_routing_skill"]; ok { skillIDs := configMap["skills"].(string) @@ -117,10 +117,20 @@ func FileContentHashResolver(configMap map[string]interface{}, filepath string) return nil } -func CampaignStatusResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter) error { +func CampaignStatusResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter, resourceName string) error { if configMap["campaign_status"] != "off" && configMap["campaign_status"] != "on" { configMap["campaign_status"] = "off" } return nil } + +func ReplyEmailAddressSelfReferenceRouteExporterResolver(configMap map[string]interface{}, exporters map[string]*ResourceExporter, resourceName string) error { + routeId := configMap["route_id"].(string) + currentRouteReference := fmt.Sprintf("${genesyscloud_routing_email_route.%s.id}", resourceName) + if routeId == currentRouteReference { + configMap["self_reference_route"] = true + configMap["route_id"] = nil + } + return nil +} diff --git a/genesyscloud/resource_exporter/resource_exporter_custom_test.go b/genesyscloud/resource_exporter/resource_exporter_custom_test.go index 3d1b4b5e5..af0cbc566 100644 --- a/genesyscloud/resource_exporter/resource_exporter_custom_test.go +++ b/genesyscloud/resource_exporter/resource_exporter_custom_test.go @@ -32,9 +32,9 @@ This is a unit test because it is just testing this single function without any func TestAccExporterCustomMemberGroup(t *testing.T) { teamID := uuid.NewString() testResults := []*customMemberGroupTest{ - &customMemberGroupTest{MemberGroupID: uuid.NewString(), MemberGroupType: "SKILLGROUP", GroupName: "test_skill_group_name", ExporterResourceType: "genesyscloud_routing_skill_group", ExpectedResult: "${genesyscloud_routing_skill_group.test_skill_group_name.id}"}, - &customMemberGroupTest{MemberGroupID: uuid.NewString(), MemberGroupType: "GROUP", GroupName: "test_group_name", ExporterResourceType: "genesyscloud_group", ExpectedResult: "${genesyscloud_group.test_group_name.id}"}, - &customMemberGroupTest{MemberGroupID: teamID, MemberGroupType: "TEAM", GroupName: "test_team_name", ExporterResourceType: "genesyscloud_team_NA", ExpectedResult: teamID}, + {MemberGroupID: uuid.NewString(), MemberGroupType: "SKILLGROUP", GroupName: "test_skill_group_name", ExporterResourceType: "genesyscloud_routing_skill_group", ExpectedResult: "${genesyscloud_routing_skill_group.test_skill_group_name.id}"}, + {MemberGroupID: uuid.NewString(), MemberGroupType: "GROUP", GroupName: "test_group_name", ExporterResourceType: "genesyscloud_group", ExpectedResult: "${genesyscloud_group.test_group_name.id}"}, + {MemberGroupID: teamID, MemberGroupType: "TEAM", GroupName: "test_team_name", ExporterResourceType: "genesyscloud_team_NA", ExpectedResult: teamID}, } for _, testResult := range testResults { @@ -60,7 +60,7 @@ func TestAccExporterCustomMemberGroup(t *testing.T) { } //Invoke the resolver - err := MemberGroupsResolver(configMap, exporters) + err := MemberGroupsResolver(configMap, exporters, testResult.ExporterResourceType) if err != nil && testResult.MemberGroupType != "TEAM" { t.Errorf("Received an unexpected error while calling MemberGroupResolver: %v", err) @@ -68,7 +68,7 @@ func TestAccExporterCustomMemberGroup(t *testing.T) { //The member_group_id should now be replaced by the expected out put with th if configMap["member_group_id"].(string) != testResult.ExpectedResult { - t.Errorf("The member_group_id set in the config map was %v,but wanted %v", configMap["member_group_id"], testResult.ExpectedResult) + t.Errorf("The member_group_id set in the config map was %v, but wanted %v", configMap["member_group_id"], testResult.ExpectedResult) } } @@ -85,7 +85,7 @@ func TestRuleSetPropertyGroup(t *testing.T) { jsonString := string(jsonData) testResults := []*propertyGroupTest{ - &propertyGroupTest{Skills: jsonString, SkillName: "test_skill_name", ExporterResourceType: "genesyscloud_routing_skill", ExpectedResult: "[\"${genesyscloud_routing_skill.test_skill_name.id}\"]"}, + {Skills: jsonString, SkillName: "test_skill_name", ExporterResourceType: "genesyscloud_routing_skill", ExpectedResult: "[\"${genesyscloud_routing_skill.test_skill_name.id}\"]"}, } for _, testResult := range testResults { @@ -110,7 +110,7 @@ func TestRuleSetPropertyGroup(t *testing.T) { } //Invoke the resolver - err := RuleSetSkillPropertyResolver(configMap, exporters) + err := RuleSetSkillPropertyResolver(configMap, exporters, testResult.ExporterResourceType) if err != nil { t.Errorf("Received an unexpected error while calling RuleSetSkillPropertyResolver: %v", err) diff --git a/genesyscloud/routing_email_route/genesyscloud_routing_email_route_init_test.go b/genesyscloud/routing_email_route/genesyscloud_routing_email_route_init_test.go index 18adeeda5..0d5a0239d 100644 --- a/genesyscloud/routing_email_route/genesyscloud_routing_email_route_init_test.go +++ b/genesyscloud/routing_email_route/genesyscloud_routing_email_route_init_test.go @@ -1,10 +1,12 @@ package routing_email_route import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "sync" "terraform-provider-genesyscloud/genesyscloud" + "terraform-provider-genesyscloud/genesyscloud/architect_flow" "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) /* @@ -30,6 +32,7 @@ func (r *registerTestInstance) registerTestResources() { providerResources["genesyscloud_routing_queue"] = genesyscloud.ResourceRoutingQueue() providerResources["genesyscloud_routing_language"] = genesyscloud.ResourceRoutingLanguage() providerResources["genesyscloud_routing_skill"] = genesyscloud.ResourceRoutingSkill() + providerResources["genesyscloud_flow"] = architect_flow.ResourceArchitectFlow() } // initTestResources initializes all test resources and data sources. diff --git a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route.go b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route.go index 129f17188..c97b7e8c5 100644 --- a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route.go +++ b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route.go @@ -3,15 +3,16 @@ package routing_email_route import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/mypurecloud/platform-client-sdk-go/v125/platformclientv2" "log" "terraform-provider-genesyscloud/genesyscloud/provider" resourceExporter "terraform-provider-genesyscloud/genesyscloud/resource_exporter" "terraform-provider-genesyscloud/genesyscloud/util" "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/mypurecloud/platform-client-sdk-go/v125/platformclientv2" + "terraform-provider-genesyscloud/genesyscloud/consistency_checker" "terraform-provider-genesyscloud/genesyscloud/util/resourcedata" @@ -53,14 +54,16 @@ func createRoutingEmailRoute(ctx context.Context, d *schema.ResourceData, meta i routingEmailRoute := getRoutingEmailRouteFromResourceData(d) + replyEmail, err := validateSdkReplyEmailAddress(d) // Checking the self_reference_route flag and routeId rules - if err := validateSdkReplyEmailAddress(d); err != nil { + if err != nil { return diag.Errorf("Error occurred while validating the reply email address when creating the record: %s", err) } + replyDomainID, replyRouteID, _ := extractReplyEmailAddressValue(d) // If the isSelfReferenceRoute() is set to false, we use the route id provided by the terraform script - if !isSelfReferenceRouteSet(d) { + if replyEmail && !isSelfReferenceRouteSet(d) { routingEmailRoute.ReplyEmailAddress = buildReplyEmailAddress(replyDomainID, replyRouteID) } @@ -74,7 +77,7 @@ func createRoutingEmailRoute(ctx context.Context, d *schema.ResourceData, meta i log.Printf("Created routing email route %s", *inboundRoute.Id) // If the isSelfReferenceRoute() is set to true we need grab the route id for the route and reapply the reply address, - if isSelfReferenceRouteSet(d) { + if replyEmail && isSelfReferenceRouteSet(d) { inboundRoute.ReplyEmailAddress = buildReplyEmailAddress(replyDomainID, *inboundRoute.Id) _, resp, err = proxy.updateRoutingEmailRoute(ctx, *inboundRoute.Id, domainId, inboundRoute) @@ -102,9 +105,9 @@ func readRoutingEmailRoute(ctx context.Context, d *schema.ResourceData, meta int if getErr != nil { if util.IsStatus404(respCode) { d.SetId("") - return retry.RetryableError(fmt.Errorf("Failed to read routing email route %s: %s", d.Id(), getErr)) + return retry.RetryableError(fmt.Errorf("failed to read routing email route %s: %s", d.Id(), getErr)) } - return retry.NonRetryableError(fmt.Errorf("Failed to read routing email route %s: %s", d.Id(), getErr)) + return retry.NonRetryableError(fmt.Errorf("failed to read routing email route %s: %s", d.Id(), getErr)) } for _, inboundRoutes := range *inboundRoutesMap { @@ -169,15 +172,19 @@ func updateRoutingEmailRoute(ctx context.Context, d *schema.ResourceData, meta i routingEmailRoute := getRoutingEmailRouteFromResourceData(d) //Checking the self_reference_route flag and routeId rules - if err := validateSdkReplyEmailAddress(d); err != nil { + replyEmail, err := validateSdkReplyEmailAddress(d) + if err != nil { return diag.Errorf("Error occurred while validating the reply email address while trying to update the record: %s", err) } + replyDomainID, replyRouteID, _ := extractReplyEmailAddressValue(d) - if isSelfReferenceRouteSet(d) { - routingEmailRoute.ReplyEmailAddress = buildReplyEmailAddress(replyDomainID, d.Id()) - } else if !isSelfReferenceRouteSet(d) { - routingEmailRoute.ReplyEmailAddress = buildReplyEmailAddress(replyDomainID, replyRouteID) + if replyEmail { + if isSelfReferenceRouteSet(d) { + routingEmailRoute.ReplyEmailAddress = buildReplyEmailAddress(replyDomainID, d.Id()) + } else if !isSelfReferenceRouteSet(d) { + routingEmailRoute.ReplyEmailAddress = buildReplyEmailAddress(replyDomainID, replyRouteID) + } } log.Printf("Updating routing email route %s", d.Id()) @@ -208,7 +215,7 @@ func deleteRoutingEmailRoute(ctx context.Context, d *schema.ResourceData, meta i log.Printf("Deleted routing email route %s", d.Id()) return nil } - return retry.NonRetryableError(fmt.Errorf("Error deleting routing email route %s: %s", d.Id(), err)) + return retry.NonRetryableError(fmt.Errorf("error deleting routing email route %s: %s", d.Id(), err)) } return retry.RetryableError(fmt.Errorf("routing email route %s still exists", d.Id())) }) diff --git a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_schema.go b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_schema.go index 3ca23a1f1..8abb10d5b 100644 --- a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_schema.go +++ b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_schema.go @@ -1,9 +1,10 @@ package routing_email_route import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "terraform-provider-genesyscloud/genesyscloud/provider" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + resourceExporter "terraform-provider-genesyscloud/genesyscloud/resource_exporter" registrar "terraform-provider-genesyscloud/genesyscloud/resource_register" ) @@ -71,14 +72,16 @@ func ResourceRoutingEmailRoute() *schema.Resource { Required: true, }, "from_email": { - Description: "The sender email to use for outgoing replies.", - Type: schema.TypeString, - Required: true, + Description: "The sender email to use for outgoing replies. This should not be set if reply_email_address is specified.", + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"reply_email_address"}, }, "queue_id": { - Description: "The queue to route the emails to. This should not be set if a flow_id is specified.", - Type: schema.TypeString, - Optional: true, + Description: "The queue to route the emails to. This should not be set if a flow_id is specified.", + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"flow_id"}, }, "priority": { Description: "The priority to use for routing.", @@ -97,15 +100,17 @@ func ResourceRoutingEmailRoute() *schema.Resource { Optional: true, }, "flow_id": { - Description: "The flow to use for processing the email. This should not be set if a queue_id is specified.", - Type: schema.TypeString, - Optional: true, + Description: "The flow to use for processing the email. This should not be set if a queue_id is specified.", + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"queue_id"}, }, "reply_email_address": { - Description: "The route to use for email replies.", - Type: schema.TypeList, - MaxItems: 1, - Optional: true, + Description: "The route to use for email replies. This should not be set if from_email or auto_bcc are specified.", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: []string{"from_email"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "domain_id": { @@ -120,7 +125,7 @@ func ResourceRoutingEmailRoute() *schema.Resource { Optional: true, }, "self_reference_route": { - Description: `Use this route as the reply email address. If true you will use the route id for this resource as the reply and you + Description: `Use this route as the reply email address. If true you will use the route id for this resource as the reply and you can not set a route. If you set this value to false (or leave the attribute off)you must set a route id.`, Type: schema.TypeBool, Required: false, @@ -131,10 +136,11 @@ func ResourceRoutingEmailRoute() *schema.Resource { }, }, "auto_bcc": { - Description: "The recipients that should be automatically blind copied on outbound emails associated with this route.", - Type: schema.TypeSet, - Optional: true, - Elem: bccEmailResource, + Description: "The recipients that should be automatically blind copied on outbound emails associated with this route. This should not be set if reply_email_address is specified.", + Type: schema.TypeSet, + Optional: true, + Elem: bccEmailResource, + ConflictsWith: []string{"reply_email_address"}, }, "spam_flow_id": { Description: "The flow to use for processing inbound emails that have been marked as spam.", @@ -160,8 +166,10 @@ func RoutingEmailRouteExporter() *resourceExporter.ResourceExporter { "reply_email_address.route_id": {RefType: "genesyscloud_routing_email_route"}, }, RemoveIfMissing: map[string][]string{ - "reply_email_address": {"route_id"}, + "reply_email_address": {"route_id", "self_reference_route"}, + }, + CustomAttributeResolver: map[string]*resourceExporter.RefAttrCustomResolver{ + "reply_email_address.self_reference_route": {ResolverFunc: resourceExporter.ReplyEmailAddressSelfReferenceRouteExporterResolver}, }, - AllowZeroValues: []string{"from_email"}, } } diff --git a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_test.go b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_test.go index 1cc57e794..d25f0878e 100644 --- a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_test.go +++ b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_test.go @@ -3,8 +3,10 @@ package routing_email_route import ( "fmt" "log" + "regexp" "strings" gcloud "terraform-provider-genesyscloud/genesyscloud" + "terraform-provider-genesyscloud/genesyscloud/architect_flow" "terraform-provider-genesyscloud/genesyscloud/provider" "terraform-provider-genesyscloud/genesyscloud/util" "testing" @@ -19,30 +21,135 @@ import ( func TestAccResourceRoutingEmailRoute(t *testing.T) { var ( - domainRes = "routing-domain1" - domainId = fmt.Sprintf("terraform.%s.com", strings.Replace(uuid.NewString(), "-", "", -1)) - queueResource = "email-queue" - queueName = "Terraform Email Queue-" + uuid.NewString() - langResource = "email-lang" - langName = "tflang" + uuid.NewString() - skillResource = "test-skill1" - skillName = "Terraform Skill" + uuid.NewString() - routeRes = "email-route1" - routeRes2 = "email-route2" - routePattern1 = "terraform1" - routePattern2 = "terraform2" - routePattern3 = "terraform3" - fromEmail1 = "terraform1@test.com" - fromEmail2 = "terraform2@test.com" - fromName1 = "John Terraform" - fromName2 = "Jane Terraform" - priority1 = "1" - bccEmail1 = "test1@" + domainId - bccEmail2 = "test2@" + domainId + domainRes = "routing-domain1" + domainId = fmt.Sprintf("terraform.%s.com", strings.Replace(uuid.NewString(), "-", "", -1)) + queueResource = "email-queue" + queueName = "Terraform Email Queue-" + uuid.NewString() + langResource = "email-lang" + langName = "tflang" + uuid.NewString() + skillResource = "test-skill1" + skillName = "Terraform Skill" + uuid.NewString() + routeRes = "email-route1" + routeRes2 = "email-route2" + routePattern1 = "terraform1" + routePattern2 = "terraform2" + routePattern3 = "terraform3" + fromEmail1 = "terraform1@test.com" + fromEmail2 = "terraform2@test.com" + fromName1 = "John Terraform" + fromName2 = "Jane Terraform" + priority1 = "1" + bccEmail1 = "test1@" + domainId + bccEmail2 = "test2@" + domainId + emailFlowResource1 = "test_flow1" + emailFlowFilePath1 = "../../examples/resources/genesyscloud_flow/inboundcall_flow_example.yaml" ) CleanupRoutingEmailDomains() + // Test error configs + resource.Test(t, resource.TestCase{ + PreCheck: func() { util.TestAccPreCheck(t) }, + ProviderFactories: provider.GetProviderFactories(providerResources, nil), + Steps: []resource.TestStep{ + { + // Confirm mutual exclusivity of reply_email_address and from_email + Config: gcloud.GenerateRoutingEmailDomainResource( + domainRes, + domainId, + util.FalseValue, + util.NullValue, + ) + generateRoutingEmailRouteResource( + routeRes+"expectFail", + "genesyscloud_routing_email_domain."+domainRes+".id", + routePattern1, + fromName1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), + generateRoutingReplyEmail( + false, + "genesyscloud_routing_email_domain."+domainRes+".id", + "genesyscloud_routing_email_route."+routeRes2+".id", + ), + ) + generateRoutingEmailRouteResource( // Second route to use as the reply_email_address + routeRes2, + "genesyscloud_routing_email_domain."+domainRes+".id", + routePattern3, + fromName1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), + generateRoutingAutoBcc(fromName2, bccEmail2), + ), + ExpectError: regexp.MustCompile("Conflicting configuration arguments"), + }, + { + // Confirm mutual exclusivity of reply_email_address and auto_bcc + Config: gcloud.GenerateRoutingEmailDomainResource( + domainRes, + domainId, + util.FalseValue, + util.NullValue, + ) + generateRoutingEmailRouteResource( + routeRes+"expectFail", + "genesyscloud_routing_email_domain."+domainRes+".id", + routePattern1, + fromName1, + generateRoutingAutoBcc(fromName1, bccEmail1), + generateRoutingReplyEmail( + false, + "genesyscloud_routing_email_domain."+domainRes+".id", + "genesyscloud_routing_email_route."+routeRes2+".id", + ), + ) + generateRoutingEmailRouteResource( // Second route to use as the reply_email_address + routeRes2, + "genesyscloud_routing_email_domain."+domainRes+".id", + routePattern3, + fromName1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), + generateRoutingAutoBcc(fromName2, bccEmail2), + ), + ExpectError: regexp.MustCompile("Conflicting configuration arguments"), + }, + { + // Confirm mutual exclusivity of flow_id and queue_id + Config: gcloud.GenerateRoutingEmailDomainResource( + domainRes, + domainId, + util.FalseValue, + util.NullValue, + ) + gcloud.GenerateRoutingQueueResourceBasic( + queueResource, + queueName, + ) + gcloud.GenerateRoutingLanguageResource( + langResource, + langName, + ) + gcloud.GenerateRoutingSkillResource( + skillResource, + skillName, + ) + architect_flow.GenerateFlowResource( + emailFlowResource1, + emailFlowFilePath1, + "", + false, + ) + generateRoutingEmailRouteResource( + routeRes+"expectFail", + "genesyscloud_routing_email_domain."+domainRes+".id", + routePattern1, + fromName1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), + generateRoutingEmailQueueSettings( + "genesyscloud_routing_queue."+queueResource+".id", + priority1, + "genesyscloud_routing_language."+langResource+".id", + "genesyscloud_routing_skill."+skillResource+".id", + ), + fmt.Sprintf("flow_id = genesyscloud_flow.%s.id", emailFlowResource1), + ), + ExpectError: regexp.MustCompile("Conflicting configuration arguments"), + }, + }, + CheckDestroy: testVerifyRoutingEmailRouteDestroyed, + }) + + // Standard acceptance tests resource.Test(t, resource.TestCase{ PreCheck: func() { util.TestAccPreCheck(t) }, ProviderFactories: provider.GetProviderFactories(providerResources, nil), @@ -59,7 +166,7 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern1, fromName1, - fromEmail1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), generateRoutingAutoBcc(fromName1, bccEmail1), ), Check: resource.ComposeTestCheckFunc( @@ -92,8 +199,6 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern2, fromName2, - fromEmail2, - generateRoutingAutoBcc(fromName2, bccEmail2), generateRoutingReplyEmail( false, "genesyscloud_routing_email_domain."+domainRes+".id", @@ -110,20 +215,24 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern3, fromName1, - fromEmail1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), + generateRoutingAutoBcc(fromName2, bccEmail2), ), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "pattern", routePattern2), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_name", fromName2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_email", fromEmail2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "auto_bcc.0.name", fromName2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "auto_bcc.0.email", bccEmail2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_email", ""), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "queue_id", "genesyscloud_routing_queue."+queueResource, "id"), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "language_id", "genesyscloud_routing_language."+langResource, "id"), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "skill_ids.0", "genesyscloud_routing_skill."+skillResource, "id"), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "priority", priority1), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.domain_id", domainId), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.route_id", "genesyscloud_routing_email_route."+routeRes2, "id"), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "pattern", routePattern3), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "from_name", fromName1), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "from_email", fromEmail1), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "auto_bcc.0.name", fromName2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "auto_bcc.0.email", bccEmail2), ), }, { @@ -147,8 +256,6 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern2, fromName2, - fromEmail2, - generateRoutingAutoBcc(fromName2, bccEmail2), generateRoutingReplyEmail( true, "genesyscloud_routing_email_domain."+domainRes+".id", @@ -165,14 +272,13 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern3, fromName1, - fromEmail1, + fmt.Sprintf("from_email = \"%s\"", fromEmail2), + generateRoutingAutoBcc(fromName2, bccEmail2), ), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "pattern", routePattern2), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_name", fromName2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_email", fromEmail2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "auto_bcc.0.name", fromName2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "auto_bcc.0.email", bccEmail2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_email", ""), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "queue_id", "genesyscloud_routing_queue."+queueResource, "id"), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "language_id", "genesyscloud_routing_language."+langResource, "id"), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "skill_ids.0", "genesyscloud_routing_skill."+skillResource, "id"), @@ -180,6 +286,11 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.domain_id", domainId), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.route_id", ""), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.self_reference_route", "true"), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "auto_bcc.0.name", fromName2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "auto_bcc.0.email", bccEmail2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "pattern", routePattern3), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "from_name", fromName1), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "from_email", fromEmail2), ), }, { @@ -203,8 +314,6 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern2, fromName2, - fromEmail2, - generateRoutingAutoBcc(fromName2, bccEmail2), generateRoutingReplyEmail( false, "genesyscloud_routing_email_domain."+domainRes+".id", @@ -221,14 +330,13 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { "genesyscloud_routing_email_domain."+domainRes+".id", routePattern3, fromName1, - fromEmail1, + fmt.Sprintf("from_email = \"%s\"", fromEmail1), + generateRoutingAutoBcc(fromName2, bccEmail2), ), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "pattern", routePattern2), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_name", fromName2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_email", fromEmail2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "auto_bcc.0.name", fromName2), - resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "auto_bcc.0.email", bccEmail2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "from_email", ""), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "queue_id", "genesyscloud_routing_queue."+queueResource, "id"), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "language_id", "genesyscloud_routing_language."+langResource, "id"), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "skill_ids.0", "genesyscloud_routing_skill."+skillResource, "id"), @@ -236,6 +344,11 @@ func TestAccResourceRoutingEmailRoute(t *testing.T) { resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.domain_id", domainId), resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.domain_id", domainId), resource.TestCheckResourceAttrPair("genesyscloud_routing_email_route."+routeRes, "reply_email_address.0.route_id", "genesyscloud_routing_email_route."+routeRes2, "id"), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "auto_bcc.0.name", fromName2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "auto_bcc.0.email", bccEmail2), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "pattern", routePattern3), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "from_name", fromName1), + resource.TestCheckResourceAttr("genesyscloud_routing_email_route."+routeRes2, "from_email", fromEmail1), ), }, { @@ -255,16 +368,14 @@ func generateRoutingEmailRouteResource( domainID string, pattern string, fromName string, - fromEmail string, otherAttrs ...string) string { return fmt.Sprintf(`resource "genesyscloud_routing_email_route" "%s" { domain_id = %s pattern = "%s" from_name = "%s" - from_email = "%s" %s } - `, resourceID, domainID, pattern, fromName, fromEmail, strings.Join(otherAttrs, "\n")) + `, resourceID, domainID, pattern, fromName, strings.Join(otherAttrs, "\n")) } func generateRoutingEmailQueueSettings( diff --git a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_utils.go b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_utils.go index 8b98f3075..a16b3cb56 100644 --- a/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_utils.go +++ b/genesyscloud/routing_email_route/resource_genesyscloud_routing_email_route_utils.go @@ -3,11 +3,12 @@ package routing_email_route import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/mypurecloud/platform-client-sdk-go/v125/platformclientv2" "strings" "terraform-provider-genesyscloud/genesyscloud/util" "terraform-provider-genesyscloud/genesyscloud/util/resourcedata" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/mypurecloud/platform-client-sdk-go/v125/platformclientv2" ) /* @@ -26,7 +27,7 @@ func getRoutingEmailRouteFromResourceData(d *schema.ResourceData) platformclient Skills: util.BuildSdkDomainEntityRefArr(d, "skill_ids"), Language: util.BuildSdkDomainEntityRef(d, "language_id"), FromName: platformclientv2.String(d.Get("from_name").(string)), - FromEmail: platformclientv2.String(d.Get("from_email").(string)), + FromEmail: buildFromEmail(d), Flow: util.BuildSdkDomainEntityRef(d, "flow_id"), AutoBcc: buildAutoBccEmailAddresses(d), SpamFlow: util.BuildSdkDomainEntityRef(d, "spam_flow_id"), @@ -35,6 +36,13 @@ func getRoutingEmailRouteFromResourceData(d *schema.ResourceData) platformclient // Build Functions +func buildFromEmail(d *schema.ResourceData) *string { + if d.Get("from_email") != "" { + return platformclientv2.String(d.Get("from_email").(string)) + } + return nil +} + func buildAutoBccEmailAddresses(d *schema.ResourceData) *[]platformclientv2.Emailaddress { if bccAddresses := d.Get("auto_bcc"); bccAddresses != nil { bccAddressList := bccAddresses.(*schema.Set).List() @@ -100,7 +108,7 @@ func flattenReplyEmailAddress(settings platformclientv2.Queueemailaddress) map[s // Helper Functions -func validateSdkReplyEmailAddress(d *schema.ResourceData) error { +func validateSdkReplyEmailAddress(d *schema.ResourceData) (bool, error) { replyEmailAddress := d.Get("reply_email_address").([]interface{}) if replyEmailAddress != nil && len(replyEmailAddress) > 0 { settingsMap := replyEmailAddress[0].(map[string]interface{}) @@ -109,14 +117,15 @@ func validateSdkReplyEmailAddress(d *schema.ResourceData) error { selfReferenceRoute := settingsMap["self_reference_route"].(bool) if selfReferenceRoute && routeID != "" { - return fmt.Errorf("can not set a reply email address route id directly, if the self_reference_route value is set to true") + return true, fmt.Errorf("can not set a reply email address route id directly, if the self_reference_route value is set to true") } if !selfReferenceRoute && routeID == "" { - return fmt.Errorf("you must provide reply email address route id if the self_reference_route value is set to false") + return true, fmt.Errorf("you must provide reply email address route id if the self_reference_route value is set to false") } + return true, nil } - return nil + return false, nil } func extractReplyEmailAddressValue(d *schema.ResourceData) (string, string, bool) { diff --git a/genesyscloud/tfexporter/genesyscloud_resource_exporter.go b/genesyscloud/tfexporter/genesyscloud_resource_exporter.go index f8825b73c..b68a2ac0a 100644 --- a/genesyscloud/tfexporter/genesyscloud_resource_exporter.go +++ b/genesyscloud/tfexporter/genesyscloud_resource_exporter.go @@ -380,7 +380,7 @@ func (g *GenesysCloudResourceExporter) buildResourceConfigMap() diag.Diagnostics exportDir, _ := getFilePath(g.d, "") err := resourceFilesWriterFunc(resource.State.ID, exportDir, exporter.CustomFileWriter.SubDirectory, jsonResult, g.meta) if err != nil { - log.Printf("An error has occured while trying invoking the RetrieveAndWriteFilesFunc for resource type %s: %v", resource.Type, err) + log.Printf("An error has occurred while trying invoking the RetrieveAndWriteFilesFunc for resource type %s: %v", resource.Type, err) } } @@ -647,7 +647,7 @@ func (g *GenesysCloudResourceExporter) retainExporterList(resources resourceExpo } } for _, removeId := range removeChan { - log.Printf("deleted removeId %v", removeId) + log.Printf("Deleted removeId %v", removeId) delete(exporter.SanitizedResourceMap, removeId) } } @@ -1001,7 +1001,7 @@ func (g *GenesysCloudResourceExporter) getResourcesForType(resType string, provi // Remove resources that weren't found in this pass for id := range removeChan { - log.Printf("deleted %v", id) + log.Printf("Deleted resource %v", id) delete(exporter.SanitizedResourceMap, id) } @@ -1167,13 +1167,13 @@ func (g *GenesysCloudResourceExporter) sanitizeConfigMap( case map[string]interface{}: // Maps are sanitized in-place currMap := val.(map[string]interface{}) - _, res := g.sanitizeConfigMap(resourceType, "", val.(map[string]interface{}), currAttr, exporters, exportingState, exportingAsHCL, false) + _, res := g.sanitizeConfigMap(resourceType, resourceName, val.(map[string]interface{}), currAttr, exporters, exportingState, exportingAsHCL, false) if !res || len(currMap) == 0 { // Remove empty maps or maps indicating they should be removed configMap[key] = nil } case []interface{}: - if arr := g.sanitizeConfigArray(resourceType, val.([]interface{}), currAttr, exporters, exportingState, exportingAsHCL); len(arr) > 0 { + if arr := g.sanitizeConfigArray(resourceType, resourceName, val.([]interface{}), currAttr, exporters, exportingState, exportingAsHCL); len(arr) > 0 { configMap[key] = arr } else { // Remove empty arrays @@ -1245,7 +1245,7 @@ func (g *GenesysCloudResourceExporter) sanitizeConfigMap( //If the exporter as has customer resolver for an attribute, invoke it. if refAttrCustomResolver, ok := exporter.CustomAttributeResolver[currAttr]; ok { log.Printf("Custom resolver invoked for attribute: %s", currAttr) - err := refAttrCustomResolver.ResolverFunc(configMap, exporters) + err := refAttrCustomResolver.ResolverFunc(configMap, exporters, resourceName) if err != nil { log.Printf("An error has occurred while trying invoke a custom resolver for attribute %s", currAttr) @@ -1267,7 +1267,7 @@ func (g *GenesysCloudResourceExporter) sanitizeConfigMap( if vStr, ok := configMap[key].(string); ok { decodedData, err := getDecodedData(vStr, currAttr) if err != nil { - log.Printf("error decoding json string: %v\n", err) + log.Printf("Error decoding JSON string: %v\n", err) configMap[key] = vStr } else { uid := uuid.NewString() @@ -1334,6 +1334,7 @@ func escapeString(strValue string) string { func (g *GenesysCloudResourceExporter) sanitizeConfigArray( resourceType string, + resourceName string, anArray []interface{}, currAttr string, exporters map[string]*resourceExporter.ResourceExporter, @@ -1346,12 +1347,12 @@ func (g *GenesysCloudResourceExporter) sanitizeConfigArray( case map[string]interface{}: // Only include in the result if sanitizeConfigMap returns true and the map is not empty currMap := val.(map[string]interface{}) - _, res := g.sanitizeConfigMap(resourceType, "", currMap, currAttr, exporters, exportingState, exportingAsHCL, false) + _, res := g.sanitizeConfigMap(resourceType, resourceName, currMap, currAttr, exporters, exportingState, exportingAsHCL, false) if res && len(currMap) > 0 { result = append(result, val) } case []interface{}: - if arr := g.sanitizeConfigArray(resourceType, val.([]interface{}), currAttr, exporters, exportingState, exportingAsHCL); len(arr) > 0 { + if arr := g.sanitizeConfigArray(resourceType, resourceName, val.([]interface{}), currAttr, exporters, exportingState, exportingAsHCL); len(arr) > 0 { result = append(result, arr) } case string: @@ -1457,7 +1458,7 @@ func (g *GenesysCloudResourceExporter) resourceIdExists(refID string) bool { return true } } - log.Printf("resource present in sanitizedconfigmap and not present in resources section %v", refID) + log.Printf("Resource present in sanitizedConfigMap and not present in resources section %v", refID) return false } return true