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

[1/2] DXCDT-445: Fix created import ID on association resources #569

Merged
merged 3 commits into from
May 9, 2023
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
10 changes: 6 additions & 4 deletions docs/resources/connection_client.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
---
page_title: "Resource: auth0_connection_client"
description: |-
With this resource, you can manage enabled clients on a connection.
With this resource, you can enable a single client on a connection.
---

# Resource: auth0_connection_client

With this resource, you can manage enabled clients on a connection.
With this resource, you can enable a single client on a connection.

!> To prevent issues, avoid using this resource together with the `auth0_connection_clients` resource.

## Example Usage

```terraform
resource "auth0_connection" "my_conn" {
name = "My-Auth0-Connection"
strategy = "auth0"
# Avoid using the enabled_clients = [...],
# if using the auth0_connection_client resource.
}

resource "auth0_client" "my_client" {
name = "My-Auth0-Client"
}

# One connection to one client association.
# To prevent issues, avoid using this resource together with the `auth0_connection_clients` resource.
resource "auth0_connection_client" "my_conn_client_assoc" {
connection_id = auth0_connection.my_conn.id
client_id = auth0_client.my_client.id
Expand Down
63 changes: 63 additions & 0 deletions docs/resources/connection_clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
page_title: "Resource: auth0_connection_clients"
description: |-
With this resource, you can manage all of the enabled clients on a connection.
---

# Resource: auth0_connection_clients

With this resource, you can manage all of the enabled clients on a connection.

!> To prevent issues, avoid using this resource together with the `auth0_connection_client` resource.

## Example Usage

```terraform
resource "auth0_connection" "my_conn" {
name = "My-Auth0-Connection"
strategy = "auth0"
}

resource "auth0_client" "my_first_client" {
name = "My-First-Auth0-Client"
}

resource "auth0_client" "my_second_client" {
name = "My-Second-Auth0-Client"
}

# One connection to many clients association.
# To prevent issues, avoid using this resource together with the `auth0_connection_client` resource.
resource "auth0_connection_clients" "my_conn_clients_assoc" {
connection_id = auth0_connection.my_conn.id
enabled_clients = [
auth0_client.my_first_client.id,
auth0_client.my_second_client.id
]
}
```

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

### Required

- `connection_id` (String) ID of the connection on which to enable the client.
- `enabled_clients` (Set of String) IDs of the clients for which the connection is enabled.

### Read-Only

- `id` (String) The ID of this resource.
- `name` (String) The name of the connection on which to enable the client.
- `strategy` (String) The strategy of the connection on which to enable the client.

## Import

Import is supported using the following syntax:

```shell
# This resource can be imported by specifying the Connection ID.
#
# Example:
terraform import auth0_connection_clients.my_conn_clients_assoc con_XXXXX:
```
4 changes: 2 additions & 2 deletions examples/resources/auth0_connection_client/resource.tf
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
resource "auth0_connection" "my_conn" {
name = "My-Auth0-Connection"
strategy = "auth0"
# Avoid using the enabled_clients = [...],
# if using the auth0_connection_client resource.
}

resource "auth0_client" "my_client" {
name = "My-Auth0-Client"
}

# One connection to one client association.
# To prevent issues, avoid using this resource together with the `auth0_connection_clients` resource.
resource "auth0_connection_client" "my_conn_client_assoc" {
connection_id = auth0_connection.my_conn.id
client_id = auth0_client.my_client.id
Expand Down
4 changes: 4 additions & 0 deletions examples/resources/auth0_connection_clients/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This resource can be imported by specifying the Connection ID.
#
# Example:
terraform import auth0_connection_clients.my_conn_clients_assoc con_XXXXX:
22 changes: 22 additions & 0 deletions examples/resources/auth0_connection_clients/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
resource "auth0_connection" "my_conn" {
name = "My-Auth0-Connection"
strategy = "auth0"
}

resource "auth0_client" "my_first_client" {
name = "My-First-Auth0-Client"
}

resource "auth0_client" "my_second_client" {
name = "My-Second-Auth0-Client"
}

# One connection to many clients association.
# To prevent issues, avoid using this resource together with the `auth0_connection_client` resource.
resource "auth0_connection_clients" "my_conn_clients_assoc" {
connection_id = auth0_connection.my_conn.id
enabled_clients = [
auth0_client.my_first_client.id,
auth0_client.my_second_client.id
]
}
7 changes: 3 additions & 4 deletions internal/auth0/connection/resource_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/auth0/go-auth0/management"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/auth0/terraform-provider-auth0/internal/mutex"
Expand All @@ -19,7 +18,7 @@ var (
errInvalidConnectionClientIDFormat = fmt.Errorf("ID must be formated as <connectionID>:<clientID>")
)

// NewClientResource will return a new auth0_connection_client resource.
// NewClientResource will return a new auth0_connection_client (1:1) resource.
func NewClientResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -52,7 +51,7 @@ func NewClientResource() *schema.Resource {
Importer: &schema.ResourceImporter{
StateContext: importConnectionClient,
},
Description: "With this resource, you can manage enabled clients on a connection.",
Description: "With this resource, you can enable a single client on a connection.",
}
}

Expand All @@ -79,7 +78,7 @@ func createConnectionClient(ctx context.Context, data *schema.ResourceData, meta
return diag.FromErr(err)
}

data.SetId(id.UniqueId())
data.SetId(connectionID + ":" + clientID)

return readConnectionClient(ctx, data, meta)
}
Expand Down
195 changes: 195 additions & 0 deletions internal/auth0/connection/resource_clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package connection

import (
"context"
"fmt"
"net/http"

"github.com/auth0/go-auth0/management"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/auth0/terraform-provider-auth0/internal/mutex"
"github.com/auth0/terraform-provider-auth0/internal/value"
)

// NewClientsResource will return a new auth0_connection_clients (1:many) resource.
func NewClientsResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"connection_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "ID of the connection on which to enable the client.",
},
"name": {
Type: schema.TypeString,
Computed: true,
Description: "The name of the connection on which to enable the client.",
},
"strategy": {
Type: schema.TypeString,
Computed: true,
Description: "The strategy of the connection on which to enable the client.",
},
"enabled_clients": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Required: true,
Description: "IDs of the clients for which the connection is enabled.",
},
},
CreateContext: createConnectionClients,
ReadContext: readConnectionClients,
UpdateContext: updateConnectionClients,
DeleteContext: deleteConnectionClients,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Description: "With this resource, you can manage all of the enabled clients on a connection.",
}
}

func createConnectionClients(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*management.Management)

connectionID := data.Get("connection_id").(string)

mutex.Global.Lock(connectionID)
defer mutex.Global.Unlock(connectionID)

connection, err := api.Connection.Read(
connectionID,
management.IncludeFields("enabled_clients", "strategy", "name"),
)
if err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}

return diag.FromErr(err)
}

// This is never nil because the enabled clients is a required parameter.
enabledClients := value.Strings(data.GetRawConfig().GetAttr("enabled_clients"))

if diagnostics := guardAgainstErasingUnwantedEnabledClients(
connection.GetID(),
*enabledClients,
connection.GetEnabledClients(),
); diagnostics.HasError() {
data.SetId("")
return diagnostics
}

data.SetId(connection.GetID())

if err := api.Connection.Update(
connectionID,
&management.Connection{EnabledClients: enabledClients},
); err != nil {
return diag.FromErr(err)
}

return readConnectionClients(ctx, data, meta)
}

func readConnectionClients(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*management.Management)

connection, err := api.Connection.Read(
data.Id(),
management.IncludeFields("enabled_clients", "strategy", "name"),
)
if err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}

return diag.FromErr(err)
}

result := multierror.Append(
data.Set("name", connection.GetName()),
data.Set("strategy", connection.GetStrategy()),
data.Set("enabled_clients", connection.GetEnabledClients()),
)

return diag.FromErr(result.ErrorOrNil())
}

func updateConnectionClients(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*management.Management)

mutex.Global.Lock(data.Id())
defer mutex.Global.Unlock(data.Id())

if err := api.Connection.Update(
data.Id(),
&management.Connection{EnabledClients: value.Strings(data.GetRawConfig().GetAttr("enabled_clients"))},
); err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}

return diag.FromErr(err)
}

return readConnectionClients(ctx, data, meta)
}

func deleteConnectionClients(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*management.Management)

mutex.Global.Lock(data.Id())
defer mutex.Global.Unlock(data.Id())

enabledClients := make([]string, 0)

if err := api.Connection.Update(
data.Id(),
&management.Connection{EnabledClients: &enabledClients},
); err != nil {
if mErr, ok := err.(management.Error); ok && mErr.Status() == http.StatusNotFound {
data.SetId("")
return nil
}

return diag.FromErr(err)
}

data.SetId("")

return nil
}

func guardAgainstErasingUnwantedEnabledClients(
connectionID string,
configEnabledClients []string,
connectionEnabledClients []string,
) diag.Diagnostics {
if len(connectionEnabledClients) == 0 {
return nil
}

if cmp.Equal(configEnabledClients, connectionEnabledClients) {
return nil
}

return diag.Diagnostics{
diag.Diagnostic{
Severity: diag.Error,
Summary: "Connection with non empty enabled clients",
Detail: cmp.Diff(configEnabledClients, connectionEnabledClients) +
fmt.Sprintf("\nThe connection already has enabled clients attached to it. "+
"Import the resource instead in order to proceed with the changes. "+
"Run: 'terraform import auth0_connection_clients.<given-name> %s'.", connectionID),
},
}
}
Loading