Skip to content

Commit

Permalink
refactor: move resource data change detectors to separate package (#521)
Browse files Browse the repository at this point in the history
This moves some functions and related tests for detecting changes to
resource data out of `provider.go` into its own package so that it's
easier to isolate the SDK provider into its own package. This was broken
out of #518
  • Loading branch information
ctreatma authored Jan 19, 2024
1 parent af0a054 commit 8146399
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 190 deletions.
55 changes: 0 additions & 55 deletions equinix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package equinix
import (
"context"
"fmt"
"reflect"
"strings"
"time"

Expand All @@ -24,15 +23,6 @@ var (
NetworkTypeListHB = strings.Join(DeviceNetworkTypesHB, ", ")
)

// resourceDataProvider provies interface to schema.ResourceData
// for convenient mocking purposes
type resourceDataProvider interface {
Get(key string) interface{}
GetOk(key string) (interface{}, bool)
HasChange(key string) bool
GetChange(key string) (interface{}, interface{})
}

// Provider returns Equinix terraform *schema.Provider
func Provider() *schema.Provider {
provider := &schema.Provider{
Expand Down Expand Up @@ -249,40 +239,6 @@ func isStringInSlice(needle string, hay []string) bool {
return false
}

func getResourceDataChangedKeys(keys []string, d resourceDataProvider) map[string]interface{} {
changed := make(map[string]interface{})
for _, key := range keys {
if v := d.Get(key); v != nil && d.HasChange(key) {
changed[key] = v
}
}
return changed
}

func getResourceDataListElementChanges(keys []string, listKeyName string, listIndex int, d resourceDataProvider) map[string]interface{} {
changed := make(map[string]interface{})
if !d.HasChange(listKeyName) {
return changed
}
old, new := d.GetChange(listKeyName)
oldList := old.([]interface{})
newList := new.([]interface{})
if len(oldList) < listIndex || len(newList) < listIndex {
return changed
}
return getMapChangedKeys(keys, oldList[listIndex].(map[string]interface{}), newList[listIndex].(map[string]interface{}))
}

func getMapChangedKeys(keys []string, old, new map[string]interface{}) map[string]interface{} {
changed := make(map[string]interface{})
for _, key := range keys {
if !reflect.DeepEqual(old[key], new[key]) {
changed[key] = new[key]
}
}
return changed
}

func isEmpty(v interface{}) bool {
switch v := v.(type) {
case int:
Expand Down Expand Up @@ -347,14 +303,3 @@ func slicesMatchCaseInsensitive(s1, s2 []string) bool {
}
return true
}

func schemaSetToMap(set *schema.Set) map[int]interface{} {
transformed := make(map[int]interface{})
if set != nil {
list := set.List()
for i := range list {
transformed[set.F(list[i])] = list[i]
}
}
return transformed
}
128 changes: 1 addition & 127 deletions equinix/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ package equinix
import (
"fmt"
"os"
"reflect"
"regexp"
"strings"
"testing"

"github.com/equinix/terraform-provider-equinix/internal/config"
"github.com/equinix/terraform-provider-equinix/internal/hashcode"

"github.com/equinix/ecx-go/v2"
"github.com/equinix/terraform-provider-equinix/internal/config"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/stretchr/testify/assert"
Expand All @@ -24,28 +21,6 @@ var (
testExternalProviders map[string]resource.ExternalProvider
)

type mockedResourceDataProvider struct {
actual map[string]interface{}
old map[string]interface{}
}

func (r mockedResourceDataProvider) Get(key string) interface{} {
return r.actual[key]
}

func (r mockedResourceDataProvider) GetOk(key string) (interface{}, bool) {
v, ok := r.actual[key]
return v, ok
}

func (r mockedResourceDataProvider) HasChange(key string) bool {
return !reflect.DeepEqual(r.old[key], r.actual[key])
}

func (r mockedResourceDataProvider) GetChange(key string) (interface{}, interface{}) {
return r.old[key], r.actual[key]
}

type mockECXClient struct {
GetUserPortsFn func() ([]ecx.Port, error)

Expand Down Expand Up @@ -176,82 +151,6 @@ func TestProvider_stringsFound_negative(t *testing.T) {
assert.False(t, result, "Given strings were found")
}

func TestProvider_resourceDataChangedKeys(t *testing.T) {
// given
keys := []string{"key", "keyTwo", "keyThree"}
rd := mockedResourceDataProvider{
actual: map[string]interface{}{
"key": "value",
"keyTwo": "newValueTwo",
},
old: map[string]interface{}{
"key": "value",
"keyTwo": "valueTwo",
},
}
expected := map[string]interface{}{
"keyTwo": "newValueTwo",
}
// when
result := getResourceDataChangedKeys(keys, rd)
// then
assert.Equal(t, expected, result, "Function returns valid key changes")
}

func TestProvider_resourceDataListElementChanges(t *testing.T) {
// given
keys := []string{"key", "keyTwo", "keyThree"}
listKeyName := "myList"
rd := mockedResourceDataProvider{
old: map[string]interface{}{
listKeyName: []interface{}{
map[string]interface{}{
"key": "value",
"keyTwo": "valueTwo",
"keyThree": 50,
},
},
},
actual: map[string]interface{}{
listKeyName: []interface{}{
map[string]interface{}{
"key": "value",
"keyTwo": "newValueTwo",
"keyThree": 100,
},
},
},
}
expected := map[string]interface{}{
"keyTwo": "newValueTwo",
"keyThree": 100,
}
// when
result := getResourceDataListElementChanges(keys, listKeyName, 0, rd)
// then
assert.Equal(t, expected, result, "Function returns valid key changes")
}

func TestProvider_mapChanges(t *testing.T) {
// given
keys := []string{"key", "keyTwo", "keyThree"}
old := map[string]interface{}{
"key": "value",
"keyTwo": "valueTwo",
}
new := map[string]interface{}{
"key": "newValue",
"keyTwo": "valueTwo",
}
expected := map[string]interface{}{
"key": "newValue",
}
// when
result := getMapChangedKeys(keys, old, new)
// then
assert.Equal(t, expected, result, "Function returns valid key changes")
}

func TestProvider_isEmpty(t *testing.T) {
// given
input := []interface{}{
Expand Down Expand Up @@ -331,31 +230,6 @@ func TestProvider_slicesMatch(t *testing.T) {
}
}

func TestProvider_schemaSetToMap(t *testing.T) {
// given
type item struct {
id string
valueOne int
valueTwo int
}
setFunc := func(v interface{}) int {
i := v.(item)
return hashcode.String(i.id)
}
items := []interface{}{
item{"id1", 100, 200},
item{"id2", 666, 999},
item{"id3", 0, 100},
}
set := schema.NewSet(setFunc, items)
// when
list := schemaSetToMap(set)
// then
assert.Equal(t, items[0], list[setFunc(items[0])])
assert.Equal(t, items[1], list[setFunc(items[1])])
assert.Equal(t, items[2], list[setFunc(items[2])])
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Test helper functions
//_______________________________________________________________________
Expand Down
5 changes: 3 additions & 2 deletions equinix/resource_ecx_l2_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/equinix/terraform-provider-equinix/internal/config"
"github.com/equinix/terraform-provider-equinix/internal/converters"
equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors"
equinix_schema "github.com/equinix/terraform-provider-equinix/internal/schema"
equinix_validation "github.com/equinix/terraform-provider-equinix/internal/validation"

"github.com/equinix/ecx-go/v2"
Expand Down Expand Up @@ -755,13 +756,13 @@ func resourceECXL2ConnectionUpdate(ctx context.Context, d *schema.ResourceData,
ecxL2ConnectionSchemaNames["Speed"],
ecxL2ConnectionSchemaNames["SpeedUnit"],
}
primaryChanges := getResourceDataChangedKeys(supportedChanges, d)
primaryChanges := equinix_schema.GetResourceDataChangedKeys(supportedChanges, d)
primaryUpdateReq := client.NewL2ConnectionUpdateRequest(d.Id())
if err := fillFabricL2ConnectionUpdateRequest(primaryUpdateReq, primaryChanges).Execute(); err != nil {
return diag.FromErr(err)
}
if redID, ok := d.GetOk(ecxL2ConnectionSchemaNames["RedundantUUID"]); ok {
secondaryChanges := getResourceDataListElementChanges(supportedChanges, ecxL2ConnectionSchemaNames["SecondaryConnection"], 0, d)
secondaryChanges := equinix_schema.GetResourceDataListElementChanges(supportedChanges, ecxL2ConnectionSchemaNames["SecondaryConnection"], 0, d)
secondaryUpdateReq := client.NewL2ConnectionUpdateRequest(redID.(string))
if err := fillFabricL2ConnectionUpdateRequest(secondaryUpdateReq, secondaryChanges).Execute(); err != nil {
return diag.FromErr(err)
Expand Down
2 changes: 1 addition & 1 deletion equinix/resource_metal_organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func resourceMetalOrganizationUpdate(d *schema.ResourceData, meta interface{}) e
meta.(*config.Config).AddModuleToMetalUserAgent(d)
client := meta.(*config.Config).Metal

changes := getResourceDataChangedKeys([]string{"name", "description", "website", "twitter", "logo", "address"}, d)
changes := equinix_schema.GetResourceDataChangedKeys([]string{"name", "description", "website", "twitter", "logo", "address"}, d)
updateRequest := &packngo.OrganizationUpdateRequest{}
for change, changeValue := range changes {
switch change {
Expand Down
7 changes: 4 additions & 3 deletions equinix/resource_network_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/equinix/terraform-provider-equinix/internal/config"
"github.com/equinix/terraform-provider-equinix/internal/converters"
equinix_schema "github.com/equinix/terraform-provider-equinix/internal/schema"
equinix_validation "github.com/equinix/terraform-provider-equinix/internal/validation"

"github.com/equinix/ne-go"
Expand Down Expand Up @@ -965,11 +966,11 @@ func resourceNetworkDeviceUpdate(ctx context.Context, d *schema.ResourceData, m
neDeviceSchemaNames["ACLTemplateUUID"], neDeviceSchemaNames["MgmtAclTemplateUuid"],
}
updateReq := client.NewDeviceUpdateRequest(d.Id())
primaryChanges := getResourceDataChangedKeys(supportedChanges, d)
primaryChanges := equinix_schema.GetResourceDataChangedKeys(supportedChanges, d)
var clusterChanges map[string]interface{}
clusterSupportedChanges := []string{neDeviceClusterSchemaNames["ClusterName"]}
if _, ok := d.GetOk(neDeviceSchemaNames["ClusterDetails"]); ok {
clusterChanges = getResourceDataListElementChanges(clusterSupportedChanges, neDeviceSchemaNames["ClusterDetails"], 0, d)
clusterChanges = equinix_schema.GetResourceDataListElementChanges(clusterSupportedChanges, neDeviceSchemaNames["ClusterDetails"], 0, d)
for key, value := range clusterChanges {
primaryChanges[key] = value
}
Expand All @@ -979,7 +980,7 @@ func resourceNetworkDeviceUpdate(ctx context.Context, d *schema.ResourceData, m
}
var secondaryChanges map[string]interface{}
if v, ok := d.GetOk(neDeviceSchemaNames["RedundantUUID"]); ok {
secondaryChanges = getResourceDataListElementChanges(supportedChanges, neDeviceSchemaNames["Secondary"], 0, d)
secondaryChanges = equinix_schema.GetResourceDataListElementChanges(supportedChanges, neDeviceSchemaNames["Secondary"], 0, d)
secondaryUpdateReq := client.NewDeviceUpdateRequest(v.(string))
if err := fillNetworkDeviceUpdateRequest(secondaryUpdateReq, secondaryChanges).Execute(); err != nil {
return diag.FromErr(err)
Expand Down
14 changes: 13 additions & 1 deletion equinix/resource_network_device_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/equinix/terraform-provider-equinix/internal/config"
equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors"
"github.com/equinix/terraform-provider-equinix/internal/hashcode"
equinix_schema "github.com/equinix/terraform-provider-equinix/internal/schema"
equinix_validation "github.com/equinix/terraform-provider-equinix/internal/validation"

"github.com/equinix/ne-go"
Expand Down Expand Up @@ -274,7 +275,7 @@ func resourceNetworkDeviceLinkUpdate(ctx context.Context, d *schema.ResourceData
client := m.(*config.Config).Ne
m.(*config.Config).AddModuleToNEUserAgent(&client, d)
var diags diag.Diagnostics
changes := getResourceDataChangedKeys([]string{
changes := equinix_schema.GetResourceDataChangedKeys([]string{
networkDeviceLinkSchemaNames["Name"], networkDeviceLinkSchemaNames["Subnet"],
networkDeviceLinkSchemaNames["Devices"], networkDeviceLinkSchemaNames["Links"],
}, d)
Expand Down Expand Up @@ -518,3 +519,14 @@ func networkDeviceLinkConnectionKey(v interface{}) string {
func networkDeviceLinkConnectionHash(v interface{}) int {
return hashcode.String(networkDeviceLinkConnectionKey(v))
}

func schemaSetToMap(set *schema.Set) map[int]interface{} {
transformed := make(map[int]interface{})
if set != nil {
list := set.List()
for i := range list {
transformed[set.F(list[i])] = list[i]
}
}
return transformed
}
46 changes: 46 additions & 0 deletions internal/schema/resourcedata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package schema

import "reflect"

// resourceDataProvider provies interface to schema.ResourceData
// for convenient mocking purposes
type resourceDataProvider interface {
Get(key string) interface{}
GetOk(key string) (interface{}, bool)
HasChange(key string) bool
GetChange(key string) (interface{}, interface{})
}

func getMapChangedKeys(keys []string, old, new map[string]interface{}) map[string]interface{} {
changed := make(map[string]interface{})
for _, key := range keys {
if !reflect.DeepEqual(old[key], new[key]) {
changed[key] = new[key]
}
}
return changed
}

func GetResourceDataChangedKeys(keys []string, d resourceDataProvider) map[string]interface{} {
changed := make(map[string]interface{})
for _, key := range keys {
if v := d.Get(key); v != nil && d.HasChange(key) {
changed[key] = v
}
}
return changed
}

func GetResourceDataListElementChanges(keys []string, listKeyName string, listIndex int, d resourceDataProvider) map[string]interface{} {
changed := make(map[string]interface{})
if !d.HasChange(listKeyName) {
return changed
}
old, new := d.GetChange(listKeyName)
oldList := old.([]interface{})
newList := new.([]interface{})
if len(oldList) < listIndex || len(newList) < listIndex {
return changed
}
return getMapChangedKeys(keys, oldList[listIndex].(map[string]interface{}), newList[listIndex].(map[string]interface{}))
}
Loading

0 comments on commit 8146399

Please sign in to comment.