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

GPE-14846: Add External Sources resource #1450

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
34 changes: 34 additions & 0 deletions docs/data-sources/externalcontacts_external_source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "genesyscloud_externalcontacts_external_source Data Source - terraform-provider-genesyscloud"
subcategory: ""
description: |-
Genesys Cloud external contacts external source data source. Select an external source by name
---

# genesyscloud_externalcontacts_external_source (Data Source)

Genesys Cloud external contacts external source data source. Select an external source by name

## Example Usage

```terraform
data "genesyscloud_externalcontacts_external_source" "external_source" {
name = "example-source-123"
active = true
link_configuration {
uri_template = "https://some.host/{{externalId.value}}"
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) external source name

### Read-Only

- `id` (String) The ID of this resource.
54 changes: 54 additions & 0 deletions docs/resources/externalcontacts_external_source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
page_title: "genesyscloud_externalcontacts_external_source Resource - terraform-provider-genesyscloud"
subcategory: ""
description: |-
Genesys Cloud external contacts external source
---
# genesyscloud_externalcontacts_external_source (Resource)

Genesys Cloud external contacts external source

## API Usage
The following Genesys Cloud APIs are used by this resource. Ensure your OAuth Client has been granted the necessary scopes and permissions to perform these operations:

* [GET /api/v2/externalcontacts/externalsources](https://developer.genesys.cloud/devapps/api-explorer#get-api-v2-externalcontacts-externalsources)
* [POST /api/v2/externalcontacts/externalsources](https://developer.genesys.cloud/devapps/api-explorer#post-api-v2-externalcontacts-externalsources)
* [GET /api/v2/externalcontacts/externalsources/{externalSourceId}](https://developer.genesys.cloud/devapps/api-explorer#get-api-v2-externalcontacts-externalsources--externalSourceId-)
* [PUT /api/v2/externalcontacts/externalsources/{externalSourceId}](https://developer.genesys.cloud/devapps/api-explorer#put-api-v2-externalcontacts-externalsources--externalSourceId-)
* [DELETE /api/v2/externalcontacts/externalsources/{externalSourceId}](https://developer.genesys.cloud/devapps/api-explorer#delete-api-v2-externalcontacts-externalsources--externalSourceId-)

## Example Usage

```terraform
resource "genesyscloud_externalcontacts_external_source" "external_source" {
name = "some-external-source"
active = true
link_configuration {
uri_template = "https://some.host/{{externalId.value}}"
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) The name of the external source.

### Optional

- `active` (Boolean) Whether the external source is active. Defaults to `true`.
Copy link
Contributor Author

@michalk-genesys michalk-genesys Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When creating new External Sources, the backing API will ignore the value from the request and always create resource with active set to true.
I thought I'd call this out, in case this is a problem for TF provider or our users.

When running the provider locally with added trace logging, we can see this message:
2025-01-06T12:15:59.181Z [WARN] Provider "provider["genesys.com/mypurecloud/genesyscloud"]" produced an unexpected new value for genesyscloud_externalcontacts_external_source.inactive_external_source, but we are tolerating it because it is using the legacy plugin SDK.
The following problems may be the cause of any confusing errors from downstream operations:
- .active: was cty.False, but now cty.True

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A change is being proposed to address this in the backend, so that the active value from the request is honoured.

- `link_configuration` (Block List, Max: 1) (see [below for nested schema](#nestedblock--link_configuration))

### Read-Only

- `id` (String) The ID of this resource.

<a id="nestedblock--link_configuration"></a>
### Nested Schema for `link_configuration`

Optional:

- `uri_template` (String)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This string allows templating, with {{}}. The only value supported for now is {{externalId.value}}. Should this be called out in the field description?
This is not mentioned in Public API docs.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If more values are supported in the future, we'll have to come back and modify the docs here, so it might be easier to be generic and let the response tell them what they did wrong.


Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
data "genesyscloud_externalcontacts_external_source" "external_source" {
name = "example-source-123"
active = true
link_configuration {
uri_template = "https://some.host/{{externalId.value}}"
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name is the only attribute in the data source schema, so you can remove all other fields from this example (you'll need to refresh the docs by running make docs after this)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* [GET /api/v2/externalcontacts/externalsources](https://developer.genesys.cloud/devapps/api-explorer#get-api-v2-externalcontacts-externalsources)
* [POST /api/v2/externalcontacts/externalsources](https://developer.genesys.cloud/devapps/api-explorer#post-api-v2-externalcontacts-externalsources)
* [GET /api/v2/externalcontacts/externalsources/{externalSourceId}](https://developer.genesys.cloud/devapps/api-explorer#get-api-v2-externalcontacts-externalsources--externalSourceId-)
* [PUT /api/v2/externalcontacts/externalsources/{externalSourceId}](https://developer.genesys.cloud/devapps/api-explorer#put-api-v2-externalcontacts-externalsources--externalSourceId-)
* [DELETE /api/v2/externalcontacts/externalsources/{externalSourceId}](https://developer.genesys.cloud/devapps/api-explorer#delete-api-v2-externalcontacts-externalsources--externalSourceId-)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "genesyscloud_externalcontacts_external_source" "external_source" {
name = "some-external-source"
active = true
link_configuration {
uri_template = "https://some.host/{{externalId.value}}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package external_contacts_external_source

import (
"context"
"fmt"
"log"
"terraform-provider-genesyscloud/genesyscloud/provider"
rc "terraform-provider-genesyscloud/genesyscloud/resource_cache"
"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/hashicorp/terraform-plugin-sdk/v2/helper/retry"
)

var (
dataSourceExternalSourceCache *rc.DataSourceCache
)

func dataSourceExternalContactsExternalSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
sdkConfig := meta.(*provider.ProviderMeta).ClientConfig
proxy := newExternalContactsExternalSourceProxy(sdkConfig)

name := d.Get("name").(string)

if dataSourceExternalSourceCache == nil {
log.Printf("Instantiating the %s data source cache object", ResourceType)
dataSourceExternalSourceCache = rc.NewDataSourceCache(sdkConfig, hydrateExternalSourceCacheFn, getExternalSourceByNameFn)
}
return util.WithRetries(ctx, 15*time.Second, func() *retry.RetryError {
externalSourceId, retryable, response, err := proxy.getExternalContactsExternalSourceIdByName(ctx, name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not actually making use of the data source cache here. Take a look at this example to see how to make use of the functions hydrateExternalSourceCacheFn and getExternalSourceByNameFn - https://github.com/MyPureCloud/terraform-provider-genesyscloud/blob/main/genesyscloud/routing_queue/data_source_genesyscloud_routing_queue.go#L33


if err != nil {
if retryable {
return retry.RetryableError(util.BuildWithRetriesApiDiagnosticError(ResourceType, fmt.Sprintf("No external sources found with the provided name %s", name), response))
}
return retry.NonRetryableError(util.BuildWithRetriesApiDiagnosticError(ResourceType, fmt.Sprintf("Error searching exteral source %s | error: %s", name, err), response))

}

d.SetId(externalSourceId)
return nil
})
}

func hydrateExternalSourceCacheFn(c *rc.DataSourceCache, ctx context.Context) error {
proxy := getExternalContactsExternalSourceProxy(c.ClientConfig)

log.Printf("Hydrating cache for data source %s", ResourceType)

allExternalSource, resp, err := proxy.getAllExternalContactsExternalSources(ctx, "")
if err != nil {
return fmt.Errorf("failed to get external source. Error: %s | API Response: %s", err.Error(), resp.String())
}

if allExternalSource == nil || len(*allExternalSource) == 0 {
log.Printf("no external source found. The cache will remain empty.")
return nil
}

for _, externalSource := range *allExternalSource {
c.Cache[*externalSource.Name] = *externalSource.Id
}

log.Printf("Cache hydration complete for data source %s", ResourceType)
return nil
}

// returns the external source id (blank if not found) and diag
func getExternalSourceByNameFn(c *rc.DataSourceCache, name string, ctx context.Context) (string, diag.Diagnostics) {
proxy := getExternalContactsExternalSourceProxy(c.ClientConfig)
externalSourceId := ""

diag := util.WithRetries(ctx, 15*time.Second, func() *retry.RetryError {
externalSourceID, retryable, response, err := proxy.getExternalContactsExternalSourceIdByName(ctx, name)
if err != nil {
errMsg := util.BuildWithRetriesApiDiagnosticError(ResourceType, fmt.Sprintf("error requesting external source %s | error %s", name, err), response)
if !retryable {
return retry.NonRetryableError(errMsg)
}
return retry.RetryableError(errMsg)
}

externalSourceId = externalSourceID
return nil
})

return externalSourceId, diag
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package external_contacts_external_source

import (
"fmt"
"terraform-provider-genesyscloud/genesyscloud/provider"
"terraform-provider-genesyscloud/genesyscloud/util"
"testing"

"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceexternalSource(t *testing.T) {
var (
resourceLabelData = "data_external_source"
resourceLabel = "resource_external_source"

resourcePath = ResourceType + "." + resourceLabel
dataResourcePath = "data." + ResourceType + "." + resourceLabelData

name = "some-external-source-" + uuid.NewString()
active = true
uri_template = "https://some.host/{{externalId.value}}"
)

resource.Test(t, resource.TestCase{
PreCheck: func() { util.TestAccPreCheck(t) },
ProviderFactories: provider.GetProviderFactories(providerResources, providerDataSources),
Steps: []resource.TestStep{
{
// Create external source with name and active properties
Config: GenerateBasicExternalSourceResource(
resourceLabel,
name,
active,
uri_template,
) + generateExternalSourceDataSource(
resourceLabelData,
name,
resourcePath,
),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(
dataResourcePath, "id",
resourcePath, "id",
),
),
},
},
})
}

func generateExternalSourceDataSource(resourceLabel, name, dependsOn string) string {
return fmt.Sprintf(`data "%s" "%s" {
name = "%s"
depends_on = [%s]
}
`, ResourceType, resourceLabel, name, dependsOn)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package external_contacts_external_source

import (
"sync"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// 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["genesyscloud_externalcontacts_external_source"] = ResourceExternalContactsExternalSource()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the constant ResourceType here and on line 34

}

// registerTestDataSources registers all data sources used in the tests.
func (r *registerTestInstance) registerTestDataSources() {
r.datasourceMapMutex.Lock()
defer r.datasourceMapMutex.Unlock()

providerDataSources["genesyscloud_externalcontacts_external_source"] = DataSourceExternalContactsExternalSource()
}

// initTestResources initializes all test resources and data sources.
func initTestResources() {
providerDataSources = make(map[string]*schema.Resource)
providerResources = make(map[string]*schema.Resource)

regInstance := &registerTestInstance{}

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 external_contacts_external_source package
initTestResources()

// Run the test suite for the external_contacts_external_source package
m.Run()
}
Loading
Loading