From 0c4f30dc4e3601579b8dc97e15d3f3fd3b584e78 Mon Sep 17 00:00:00 2001
From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com>
Date: Fri, 26 Aug 2022 19:03:20 +0200
Subject: [PATCH] Fix issue with importing auth0_organization_member (#302)

* Fix issue with importing auth0_organization_connection

* Fix issue with importing auth0_organization_member
---
 docs/resources/organization_member.md         |  8 +--
 .../auth0_organization_member/import.sh       |  8 +--
 .../resource_auth0_organization_member.go     | 41 ++++++++++++-
 ...resource_auth0_organization_member_test.go | 57 ++++++++++++++++++-
 4 files changed, 100 insertions(+), 14 deletions(-)

diff --git a/docs/resources/organization_member.md b/docs/resources/organization_member.md
index 8730afcb9..dc17fd95d 100644
--- a/docs/resources/organization_member.md
+++ b/docs/resources/organization_member.md
@@ -59,11 +59,9 @@ resource "auth0_organization_member" "my_org_member" {
 Import is supported using the following syntax:
 
 ```shell
-# As this is not a resource identifiable by an ID within the Auth0 Management API,
-# organization members can be imported using a random string.
-#
-# We recommend [Version 4 UUID](https://www.uuidgenerator.net/version4)
+# This resource can be imported by specifying the
+# organization ID and user ID separated by ":".
 #
 # Example:
-terraform import auth0_organization_member.my_org_member 11f4a21b-011a-312d-9217-e291caca36c5
+terraform import auth0_organization_member.my_org_member "org_XXXXX:auth0|XXXXX"
 ```
diff --git a/examples/resources/auth0_organization_member/import.sh b/examples/resources/auth0_organization_member/import.sh
index 11eb55f5d..1e33e6b18 100644
--- a/examples/resources/auth0_organization_member/import.sh
+++ b/examples/resources/auth0_organization_member/import.sh
@@ -1,7 +1,5 @@
-# As this is not a resource identifiable by an ID within the Auth0 Management API,
-# organization members can be imported using a random string.
-#
-# We recommend [Version 4 UUID](https://www.uuidgenerator.net/version4)
+# This resource can be imported by specifying the
+# organization ID and user ID separated by ":".
 #
 # Example:
-terraform import auth0_organization_member.my_org_member 11f4a21b-011a-312d-9217-e291caca36c5
+terraform import auth0_organization_member.my_org_member "org_XXXXX:auth0|XXXXX"
diff --git a/internal/provider/resource_auth0_organization_member.go b/internal/provider/resource_auth0_organization_member.go
index 342efff1e..632b8262e 100644
--- a/internal/provider/resource_auth0_organization_member.go
+++ b/internal/provider/resource_auth0_organization_member.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"net/http"
+	"strings"
 
 	"github.com/auth0/go-auth0/management"
 	"github.com/hashicorp/go-multierror"
@@ -12,6 +13,11 @@ import (
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 )
 
+var (
+	errEmptyOrganizationMemberID         = fmt.Errorf("ID cannot be empty")
+	errInvalidOrganizationMemberIDFormat = fmt.Errorf("ID must be formated as <organizationID>:<userID>")
+)
+
 func newOrganizationMember() *schema.Resource {
 	return &schema.Resource{
 		Description:   "This resource is used to manage the assignment of members and their roles within an organization.",
@@ -20,7 +26,7 @@ func newOrganizationMember() *schema.Resource {
 		UpdateContext: updateOrganizationMember,
 		DeleteContext: deleteOrganizationMember,
 		Importer: &schema.ResourceImporter{
-			StateContext: schema.ImportStatePassthroughContext,
+			StateContext: importOrganizationMember,
 		},
 		Schema: map[string]*schema.Schema{
 			"organization_id": {
@@ -43,6 +49,35 @@ func newOrganizationMember() *schema.Resource {
 	}
 }
 
+func importOrganizationMember(
+	_ context.Context,
+	data *schema.ResourceData,
+	_ interface{},
+) ([]*schema.ResourceData, error) {
+	rawID := data.Id()
+	if rawID == "" {
+		return nil, errEmptyOrganizationMemberID
+	}
+
+	if !strings.Contains(rawID, ":") {
+		return nil, errInvalidOrganizationMemberIDFormat
+	}
+
+	idPair := strings.Split(rawID, ":")
+	if len(idPair) != 2 {
+		return nil, errInvalidOrganizationMemberIDFormat
+	}
+
+	result := multierror.Append(
+		data.Set("organization_id", idPair[0]),
+		data.Set("user_id", idPair[1]),
+	)
+
+	data.SetId(resource.UniqueId())
+
+	return []*schema.ResourceData{data}, result.ErrorOrNil()
+}
+
 func createOrganizationMember(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
 	userID := d.Get("user_id").(string)
 	orgID := d.Get("organization_id").(string)
@@ -106,7 +141,7 @@ func removeMemberRoles(orgID string, userID string, roles []interface{}, m inter
 func addMemberRoles(orgID string, userID string, roles []interface{}, m interface{}) error {
 	api := m.(*management.Management)
 
-	rolesToAssign := []string{}
+	var rolesToAssign []string
 	for _, r := range roles {
 		rolesToAssign = append(rolesToAssign, r.(string))
 	}
@@ -133,7 +168,7 @@ func readOrganizationMember(ctx context.Context, d *schema.ResourceData, m inter
 		return diag.FromErr(err)
 	}
 
-	rolesToSet := []interface{}{}
+	var rolesToSet []interface{}
 	for _, role := range roles.Roles {
 		rolesToSet = append(rolesToSet, role.ID)
 	}
diff --git a/internal/provider/resource_auth0_organization_member_test.go b/internal/provider/resource_auth0_organization_member_test.go
index 64fdec260..f8e889fe5 100644
--- a/internal/provider/resource_auth0_organization_member_test.go
+++ b/internal/provider/resource_auth0_organization_member_test.go
@@ -1,10 +1,14 @@
 package provider
 
 import (
+	"context"
+	"fmt"
 	"strings"
 	"testing"
 
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/stretchr/testify/assert"
 
 	"github.com/auth0/terraform-provider-auth0/internal/template"
 )
@@ -115,8 +119,59 @@ resource auth0_user user {
 	password = "MyPass123$"
 }
 
-resource auth0_organization some_org{
+resource auth0_organization some_org {
 	name = "some-org-{{.testName}}"
 	display_name = "{{.testName}}"
 }
 `
+
+func TestImportOrganizationMember(t *testing.T) {
+	var testCases = []struct {
+		testName               string
+		givenID                string
+		expectedOrganizationID string
+		expectedUserID         string
+		expectedError          error
+	}{
+		{
+			testName:               "it correctly parses the resource ID",
+			givenID:                "org_1234:auth0|62d82",
+			expectedOrganizationID: "org_1234",
+			expectedUserID:         "auth0|62d82",
+		},
+		{
+			testName:      "it fails when the given ID is empty",
+			givenID:       "",
+			expectedError: fmt.Errorf("ID cannot be empty"),
+		},
+		{
+			testName:      "it fails when the given ID does not have \":\" as a separator",
+			givenID:       "org_1234auth0|62d82",
+			expectedError: fmt.Errorf("ID must be formated as <organizationID>:<userID>"),
+		},
+		{
+			testName:      "it fails when the given ID has too many separators",
+			givenID:       "org_1234:auth0|62d82:",
+			expectedError: fmt.Errorf("ID must be formated as <organizationID>:<userID>"),
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.testName, func(t *testing.T) {
+			data := schema.TestResourceDataRaw(t, newOrganizationMember().Schema, nil)
+			data.SetId(testCase.givenID)
+
+			actualData, err := importOrganizationMember(context.Background(), data, nil)
+
+			if testCase.expectedError != nil {
+				assert.EqualError(t, err, testCase.expectedError.Error())
+				assert.Nil(t, actualData)
+				return
+			}
+
+			assert.Equal(t, actualData[0].Get("organization_id").(string), testCase.expectedOrganizationID)
+			assert.Equal(t, actualData[0].Get("user_id").(string), testCase.expectedUserID)
+			assert.NotEqual(t, actualData[0].Id(), testCase.givenID)
+		})
+	}
+}