diff --git a/genesyscloud/simple_routing_queue/data_source_genesyscloud_simple_routing_queue.go b/genesyscloud/simple_routing_queue/data_source_genesyscloud_simple_routing_queue.go new file mode 100644 index 000000000..fe8dfe412 --- /dev/null +++ b/genesyscloud/simple_routing_queue/data_source_genesyscloud_simple_routing_queue.go @@ -0,0 +1,39 @@ +package simple_routing_queue + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + gcloud "terraform-provider-genesyscloud/genesyscloud" + "time" +) + +/* + The data_source_genesyscloud_simple_routing_queue.go contains the data source implementation + for the resource. +*/ + +// dataSourceSimpleRoutingQueueRead retrieves by search term the id in question +func dataSourceSimpleRoutingQueueRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // CREATE-TODO 1: Get an instance of our proxy + + // CREATE-TODO 2: Grab our queue name from the schema.ResourceData object (done) + name := d.Get("name").(string) + + log.Printf("Finding queue by name '%s'", name) + return gcloud.WithRetries(ctx, 15*time.Second, func() *resource.RetryError { + // CREATE-TODO 3: Call to the proxy function getRoutingQueueIdByName(context.Context, string) + // This function returns values in the following order: queueId (string), retryable (bool), err (error) + + // CREATE-TODO 4: If the error is not nil, and retryable equals false, return a resource.NonRetryableError + // to let the user know that an error occurred + + // CREATE-TODO 5: If retryable equals true, return a resource.RetryableError and let them know the queue could not be found with that name + + // CREATE-TODO 6: If we made it this far, we can set the queue ID in the schema.ResourceData object, and return nil + + return nil + }) +} diff --git a/genesyscloud/simple_routing_queue/data_source_genesyscloud_simple_routing_queue_test.go b/genesyscloud/simple_routing_queue/data_source_genesyscloud_simple_routing_queue_test.go new file mode 100644 index 000000000..16b1a131c --- /dev/null +++ b/genesyscloud/simple_routing_queue/data_source_genesyscloud_simple_routing_queue_test.go @@ -0,0 +1,54 @@ +package simple_routing_queue + +import ( + "fmt" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + gcloud "terraform-provider-genesyscloud/genesyscloud" + "testing" +) + +func TestAccDataSourceSimpleRoutingQueue(t *testing.T) { + var ( + resourceId = "queue_resource" + dataSourceId = "queue_data" + simpleQueueName = "Create2023 queue " + uuid.NewString() + + fullPathToResource = resourceName + "." + resourceId + fullPathToDataSource = "data." + resourceName + "." + dataSourceId + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { gcloud.TestAccPreCheck(t) }, + ProviderFactories: gcloud.GetProviderFactories(nil, nil), + Steps: []resource.TestStep{ + { + Config: generateSimpleRoutingQueueResource( + resourceId, + simpleQueueName, + "null", + "null", + ) + generateSimpleRoutingQueueDataSource( + dataSourceId, + simpleQueueName, + fullPathToResource, + ), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair( + fullPathToDataSource, "id", + fullPathToResource, "id", + ), + ), + }, + }, + }) +} + +func generateSimpleRoutingQueueDataSource(dataSourceId, queueName, dependsOn string) string { + return fmt.Sprintf(` +data "genesyscloud_simple_routing_queue" "%s" { + name = "%s" + depends_on = [%s] +} +`, dataSourceId, queueName, dependsOn) +} diff --git a/genesyscloud/simple_routing_queue/genesyscloud_simple_routing_queue_init_test.go b/genesyscloud/simple_routing_queue/genesyscloud_simple_routing_queue_init_test.go new file mode 100644 index 000000000..71507d6d6 --- /dev/null +++ b/genesyscloud/simple_routing_queue/genesyscloud_simple_routing_queue_init_test.go @@ -0,0 +1,64 @@ +package simple_routing_queue + +import ( + "sync" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +/* + The genesyscloud_simple_routing_queue_init_test.go file is used to initialize the data sources and resources + used in testing the simple_routing_queues resource. + + Please make sure you register ALL resources and data sources your test cases will use. +*/ + +// providerDataSources holds a map of all registered datasources +var providerDataSources map[string]*schema.Resource + +// providerResources holds a map of all registered resources +var providerResources map[string]*schema.Resource + +type registerTestInstance struct { + resourceMapMutex sync.RWMutex + datasourceMapMutex sync.RWMutex +} + +// registerTestResources registers all resources used in the tests +func (r *registerTestInstance) registerTestResources() { + r.resourceMapMutex.Lock() + defer r.resourceMapMutex.Unlock() + + providerResources[resourceName] = ResourceSimpleRoutingQueue() + +} + +// registerTestDataSources registers all data sources used in the tests. +func (r *registerTestInstance) registerTestDataSources() { + r.datasourceMapMutex.Lock() + defer r.datasourceMapMutex.Unlock() + + providerDataSources[resourceName] = DataSourceSimpleRoutingQueue() + +} + +// initTestResources initializes all test resources and data sources. +func initTestResources() { + providerDataSources = make(map[string]*schema.Resource) + providerResources = make(map[string]*schema.Resource) + + regInstance := ®isterTestInstance{} + + regInstance.registerTestDataSources() + regInstance.registerTestResources() +} + +// TestMain is a "setup" function called by the testing framework when run the test +func TestMain(m *testing.M) { + // Run setup function before starting the test suite for simple_routing_queue package + initTestResources() + + // Run the test suite for the simple_routing_queue package + m.Run() +} diff --git a/genesyscloud/simple_routing_queue/genesyscloud_simple_routing_queue_proxy.go b/genesyscloud/simple_routing_queue/genesyscloud_simple_routing_queue_proxy.go new file mode 100644 index 000000000..8221a3ee9 --- /dev/null +++ b/genesyscloud/simple_routing_queue/genesyscloud_simple_routing_queue_proxy.go @@ -0,0 +1,124 @@ +package simple_routing_queue + +import ( + "context" + "fmt" + "github.com/mypurecloud/platform-client-sdk-go/v105/platformclientv2" +) + +type createRoutingQueueFunc func(context.Context, *simpleRoutingQueueProxy, *platformclientv2.Createqueuerequest) (*platformclientv2.Queue, *platformclientv2.APIResponse, error) +type getRoutingQueueFunc func(context.Context, *simpleRoutingQueueProxy, string) (*platformclientv2.Queue, int, error) +type updateRoutingQueueFunc func(context.Context, *simpleRoutingQueueProxy, string, *platformclientv2.Queuerequest) (*platformclientv2.Queue, *platformclientv2.APIResponse, error) +type deleteRoutingQueueFunc func(context.Context, *simpleRoutingQueueProxy, string, bool) (*platformclientv2.APIResponse, error) +type getRoutingQueueIdByNameFunc func(context.Context, *simpleRoutingQueueProxy, string) (id string, retryable bool, err error) + +var internalProxy *simpleRoutingQueueProxy + +type simpleRoutingQueueProxy struct { + routingApi *platformclientv2.RoutingApi + createRoutingQueueAttr createRoutingQueueFunc + getRoutingQueueAttr getRoutingQueueFunc + getRoutingQueueIdByNameAttr getRoutingQueueIdByNameFunc + updateRoutingQueueAttr updateRoutingQueueFunc + deleteRoutingQueueAttr deleteRoutingQueueFunc +} + +// newSimpleRoutingQueueProxy initializes the simple routing queue proxy with all the data needed to communicate with Genesys Cloud +func newSimpleRoutingQueueProxy(clientConfig *platformclientv2.Configuration) *simpleRoutingQueueProxy { + api := platformclientv2.NewRoutingApiWithConfig(clientConfig) + return &simpleRoutingQueueProxy{ + routingApi: api, + createRoutingQueueAttr: createRoutingQueueFn, + getRoutingQueueAttr: getRoutingQueueFn, + getRoutingQueueIdByNameAttr: getRoutingQueueIdByNameFn, + updateRoutingQueueAttr: updateRoutingQueueFn, + deleteRoutingQueueAttr: deleteRoutingQueueFn, + } +} + +// getSimpleRoutingQueueProxy acts as a singleton to for the internalProxy. It also ensures +// that we can still proxy our tests by directly setting internalProxy package variable +func getSimpleRoutingQueueProxy(clientConfig *platformclientv2.Configuration) *simpleRoutingQueueProxy { + if internalProxy == nil { + internalProxy = newSimpleRoutingQueueProxy(clientConfig) + } + return internalProxy +} + +// createRoutingQueue creates a Genesys Cloud Routing Queue +func (p *simpleRoutingQueueProxy) createRoutingQueue(ctx context.Context, queue *platformclientv2.Createqueuerequest) (*platformclientv2.Queue, *platformclientv2.APIResponse, error) { + return p.createRoutingQueueAttr(ctx, p, queue) +} + +// getRoutingQueue retrieves a Genesys Cloud Routing Queue by ID +func (p *simpleRoutingQueueProxy) getRoutingQueue(ctx context.Context, id string) (*platformclientv2.Queue, int, error) { + return p.getRoutingQueueAttr(ctx, p, id) +} + +// getRoutingQueueIdByName retrieves a Genesys Cloud Routing Queue ID by its name +func (p *simpleRoutingQueueProxy) getRoutingQueueIdByName(ctx context.Context, name string) (string, bool, error) { + return p.getRoutingQueueIdByNameAttr(ctx, p, name) +} + +// updateRoutingQueue updates a Genesys Cloud Routing Queue +func (p *simpleRoutingQueueProxy) updateRoutingQueue(ctx context.Context, id string, queue *platformclientv2.Queuerequest) (*platformclientv2.Queue, *platformclientv2.APIResponse, error) { + return p.updateRoutingQueueAttr(ctx, p, id, queue) +} + +// deleteRoutingQueue deletes a Genesys Cloud Routing Queue +func (p *simpleRoutingQueueProxy) deleteRoutingQueue(ctx context.Context, id string, forceDelete bool) (*platformclientv2.APIResponse, error) { + return p.deleteRoutingQueueAttr(ctx, p, id, forceDelete) +} + +// createRoutingQueueFn is an implementation function for creating a Genesys Cloud Routing Queue +func createRoutingQueueFn(ctx context.Context, proxy *simpleRoutingQueueProxy, queue *platformclientv2.Createqueuerequest) (*platformclientv2.Queue, *platformclientv2.APIResponse, error) { + sdkQueue, response, err := proxy.routingApi.PostRoutingQueues(*queue) + if err != nil { + return nil, nil, fmt.Errorf("failed to create queue: %v", err) + } + return sdkQueue, response, err +} + +func getRoutingQueueFn(ctx context.Context, proxy *simpleRoutingQueueProxy, id string) (*platformclientv2.Queue, int, error) { + queue, response, err := proxy.routingApi.GetRoutingQueue(id) + if err != nil { + return nil, response.StatusCode, fmt.Errorf("failed to get routing queue by id '%s': %v", id, err) + } + return queue, 0, err +} + +func getRoutingQueueIdByNameFn(ctx context.Context, proxy *simpleRoutingQueueProxy, name string) (string, bool, error) { + for pageNum := 1; ; pageNum++ { + const pageSize = 100 + queues, _, getErr := proxy.routingApi.GetRoutingQueues(pageNum, pageSize, name, "", nil, nil, nil, false) + if getErr != nil { + return "", false, getErr + } + + if queues.Entities == nil || len(*queues.Entities) == 0 { + return "", true, fmt.Errorf("no routing queues found with name %s", name) + } + + for _, queue := range *queues.Entities { + if queue.Name != nil && *queue.Name == name { + return *queue.Id, false, nil + } + } + } +} + +func updateRoutingQueueFn(ctx context.Context, proxy *simpleRoutingQueueProxy, id string, body *platformclientv2.Queuerequest) (*platformclientv2.Queue, *platformclientv2.APIResponse, error) { + queue, response, err := proxy.routingApi.PutRoutingQueue(id, *body) + if err != nil { + return nil, nil, fmt.Errorf("failed to update queue %s: %v", id, err) + } + return queue, response, err +} + +func deleteRoutingQueueFn(ctx context.Context, proxy *simpleRoutingQueueProxy, id string, forceDelete bool) (*platformclientv2.APIResponse, error) { + response, err := proxy.routingApi.DeleteRoutingQueue(id, forceDelete) + if err != nil { + return nil, fmt.Errorf("failed to delete queue '%s': %v", id, err) + } + return response, err +} diff --git a/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue.go b/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue.go new file mode 100644 index 000000000..17d540007 --- /dev/null +++ b/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue.go @@ -0,0 +1,116 @@ +package simple_routing_queue + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + gcloud "terraform-provider-genesyscloud/genesyscloud" + "terraform-provider-genesyscloud/genesyscloud/consistency_checker" + "time" +) + +/* +The resource_genesyscloud_simple_routing_queue.go contains all of the methods that perform the core logic for a resource. +In general a resource should have a approximately 5 methods in it: + +1. A create.... function that the resource will use to create a Genesys Cloud object (e.g. genesyscloud_simple_routing_queue) +2. A read.... function that looks up a single resource. +3. An update... function that updates a single resource. +4. A delete.... function that deletes a single resource. + +Two things to note: + +1. All code in these methods should be focused on getting data in and out of Terraform. All code that is used for interacting + with a Genesys API should be encapsulated into a proxy class contained within the package. + +2. In general, to keep this file somewhat manageable, if you find yourself with a number of helper functions move them to a +utils file in the package. This will keep the code manageable and easy to work through. +*/ + +// createSimpleRoutingQueue is used by the genesyscloud_simple_routing_queue resource to create a simple queue in Genesys cloud. +func createSimpleRoutingQueue(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // CREATE-TODO 1: Get an instance of the proxy (example can be found in the delete method below) + + // CREATE-TODO 2: Create variables for each field in our schema.ResourceData object + // Example: + name := d.Get("name").(string) + + log.Printf("Creating simple queue %s", name) + + // CREATE-TODO 3: Create a queue struct using the Genesys Cloud platform go sdk + + // CREATE-TODO 4: Call the proxy function to create our queue. The proxy function we want to use here is createRoutingQueue(ctx context.Context, queue *platformclientv2.Createqueuerequest) + // Note: We won't need the response object returned. Also, don't forget about error handling! + + // CREATE-TODO 5: Set ID in the schema.ResourceData object + + return readSimpleRoutingQueue(ctx, d, meta) +} + +// readSimpleRoutingQueue is used by the genesyscloud_simple_routing_queue resource to read a simple queue from Genesys cloud. +func readSimpleRoutingQueue(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // CREATE-TODO 1: Get an instance of the proxy + + log.Printf("Reading simple queue %s", d.Id()) + return gcloud.WithRetriesForRead(ctx, d, func() *resource.RetryError { + /* + CREATE-TODO 2: Call the proxy function getRoutingQueue(ctx context.Context, id string) to find our queue, passing in the ID from the resource data object + The returned value are: Queue (*platformclientv2.Queue), Status Code (int), error + If the error is not nil, we should pass the status code to the function gcloud.IsStatus404ByInt(int) + If the status code is 404, return a resource.RetryableError. Otherwise, it should be a NonRetryableError + */ + + // Define consistency checker + cc := consistency_checker.NewConsistencyCheck(ctx, d, meta, ResourceSimpleRoutingQueue()) + + // CREATE-TODO 3: Set our values in the schema resource data, based on the values in the Queue object returned from the API + + return cc.CheckState() + }) +} + +// updateSimpleRoutingQueue is used by the genesyscloud_simple_routing_queue resource to update a simple queue in Genesys cloud. +func updateSimpleRoutingQueue(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + log.Printf("Updating simple queue %s", d.Id()) + // CREATE-TODO 1: Get an instance of the proxy + + // CREATE-TODO 2: Create variables for each field in our schema.ResourceData object + + // CREATE-TODO 3: Create a queue struct using the Genesys Cloud platform go sdk + + // CREATE-TODO 4: Call the proxy function updateRoutingQueue(context.Context, id string, *platformclientv2.Queuerequest) to update our queue + + return readSimpleRoutingQueue(ctx, d, meta) +} + +// deleteSimpleRoutingQueue is used by the genesyscloud_simple_routing_queue resource to delete a simple queue from Genesys cloud. +func deleteSimpleRoutingQueue(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // CREATE-TODO 1: Get an instance of the proxy (done) + sdkConfig := meta.(*gcloud.ProviderMeta).ClientConfig + proxy := getSimpleRoutingQueueProxy(sdkConfig) + + /* + CREATE-TODO 2: Call the proxy function deleteRoutingQueue(ctx context.Context, id string) + Again, we won't be needing the returned response object + */ + + log.Printf("Deleting simple queue %s", d.Id()) + // Check that queue has been deleted by trying to get it from the API + return gcloud.WithRetries(ctx, 30*time.Second, func() *resource.RetryError { + _, respCode, err := proxy.getRoutingQueue(ctx, d.Id()) + + if err == nil { + return resource.NonRetryableError(fmt.Errorf("error deleting routing queue %s: %s", d.Id(), err)) + } + if gcloud.IsStatus404ByInt(respCode) { + // Success: Routing Queue deleted + log.Printf("Deleted routing queue %s", d.Id()) + return nil + } + + return resource.RetryableError(fmt.Errorf("routing queue %s still exists", d.Id())) + }) +} diff --git a/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue_schema.go b/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue_schema.go new file mode 100644 index 000000000..0431dd744 --- /dev/null +++ b/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue_schema.go @@ -0,0 +1,54 @@ +package simple_routing_queue + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + registrar "terraform-provider-genesyscloud/genesyscloud/resource_register" + // gcloud "terraform-provider-genesyscloud/genesyscloud" +) + +const resourceName = "genesyscloud_simple_routing_queue" + +func SetRegistrar(l registrar.Registrar) { + l.RegisterResource(resourceName, ResourceSimpleRoutingQueue()) + l.RegisterDataSource(resourceName, DataSourceSimpleRoutingQueue()) +} + +func ResourceSimpleRoutingQueue() *schema.Resource { + return &schema.Resource{ + Description: "Genesys Cloud Simple Routing Queue", + + // CREATE-TODO: Specify our our functions that we defined in resource_genesyscloud_simple_routing_queue.go for performing CRUD operations. + // For example: + // ReadContext: gcloud.ReadWithPooledClient(readSimpleRoutingQueue) + // (The line to import the gcloud package is commented out above - you may have to uncomment this line) + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + SchemaVersion: 1, + Schema: map[string]*schema.Schema{ + /* + CREATE-TODO: Define the following three fields: + 1. "name" | type: string | required | description: "The name of our routing queue." + 2. "calling_party_name" | type: string | optional | description: "The name to use for caller identification for outbound calls from this queue." + 3. "enable_transcription" | type: boolean | optional | description: "Indicates whether voice transcription is enabled for this queue." + Note: The field "enable_transcription" is also Computed. This lets the provider know that the API will compute and return a value, should + the user not specify one in the resource config. + */ + }, + } +} + +func DataSourceSimpleRoutingQueue() *schema.Resource { + return &schema.Resource{ + Description: "Data source for Genesys Cloud Simple Routing Queues.", + // CREATE-TODO: As above, specify the function dataSourceSimpleRoutingQueueRead as the ReadContext of this Resource object + + Schema: map[string]*schema.Schema{ + /* + CREATE-TODO: Define the only field in our data source: + "name" | type: string | required | description: "The name of our routing queue." + */ + }, + } +} diff --git a/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue_test.go b/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue_test.go new file mode 100644 index 000000000..da091840e --- /dev/null +++ b/genesyscloud/simple_routing_queue/resource_genesyscloud_simple_routing_queue_test.go @@ -0,0 +1,51 @@ +package simple_routing_queue + +import ( + "fmt" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "strconv" + gcloud "terraform-provider-genesyscloud/genesyscloud" + "testing" +) + +func TestAccResourceSimpleRoutingQueue(t *testing.T) { + var ( + resourceId = "queue" + name = "Create 2023 Queue " + uuid.NewString() + callingPartyName = "Example Inc." + enableTranscription = "true" + + fullResourcePath = resourceName + "." + resourceId + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { gcloud.TestAccPreCheck(t) }, + ProviderFactories: gcloud.GetProviderFactories(providerResources, providerDataSources), + Steps: []resource.TestStep{ + { + Config: generateSimpleRoutingQueueResource( + resourceId, + name, + strconv.Quote(callingPartyName), + enableTranscription, + ), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fullResourcePath, "name", name), + resource.TestCheckResourceAttr(fullResourcePath, "calling_party_name", callingPartyName), + resource.TestCheckResourceAttr(fullResourcePath, "enable_transcription", "true"), + ), + }, + }, + }) +} + +func generateSimpleRoutingQueueResource(resourceId, name, callingPartyName, enableTranscription string) string { + return fmt.Sprintf(` +resource "%s" "%s" { + name = "%s" + calling_party_name = %s + enable_transcription = %s +} +`, resourceName, resourceId, name, callingPartyName, enableTranscription) +} diff --git a/main.go b/main.go index 9402dbdb1..862948d61 100644 --- a/main.go +++ b/main.go @@ -75,22 +75,23 @@ type RegisterInstance struct { func registerResources() { - reg_instance := &RegisterInstance{} + regInstance := &RegisterInstance{} - pat.SetRegistrar(reg_instance) + pat.SetRegistrar(regInstance) - ob.SetRegistrar(reg_instance) - gcloud.SetRegistrar(reg_instance) - obAttemptLimit.SetRegistrar(reg_instance) - obContactList.SetRegistrar(reg_instance) - obRuleset.SetRegistrar(reg_instance) - scripts.SetRegistrar(reg_instance) - externalContacts.SetRegistrar(reg_instance) + ob.SetRegistrar(regInstance) + gcloud.SetRegistrar(regInstance) + obAttemptLimit.SetRegistrar(regInstance) + obContactList.SetRegistrar(regInstance) + obRuleset.SetRegistrar(regInstance) + scripts.SetRegistrar(regInstance) + externalContacts.SetRegistrar(regInstance) resourceExporter.SetRegisterExporter(resourceExporters) + /* CREATE-TODO: Import the simple_routing_queue package and call it's SetRegistrar function, as above. */ + // setting resources for Use cases like TF export where provider is used in resource classes. - //tfexp.GetRegistrarresources() - tfexp.SetRegistrar(reg_instance) + tfexp.SetRegistrar(regInstance) registrar.SetResources(providerResources, providerDataSources) }