Skip to content

Commit

Permalink
refactor: use ResourceData interface
Browse files Browse the repository at this point in the history
Replaces `*schema.ResourceData` type with `schemautil.ResourceData` in the shared code.
The interface allows to write tests with mockery.
  • Loading branch information
byashimov committed Jan 22, 2025
1 parent bd8b7c4 commit ad2f2ad
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 30 deletions.
2 changes: 1 addition & 1 deletion internal/schemautil/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func SetTagsTerraformProperties(t map[string]string) []map[string]interface{} {
return tags
}

func GetTagsFromSchema(d *schema.ResourceData) map[string]string {
func GetTagsFromSchema(d ResourceData) map[string]string {
tags := make(map[string]string)

for _, tag := range d.Get("tag").(*schema.Set).List() {
Expand Down
2 changes: 2 additions & 0 deletions internal/schemautil/resourcedata.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package schemautil

import (
"context"
"time"

avngen "github.com/aiven/go-client-codegen"
"github.com/hashicorp/go-cty/cty"
Expand All @@ -21,6 +22,7 @@ type ResourceData interface {
GetOk(string) (any, bool)
GetRawConfig() cty.Value
HasChange(string) bool
Timeout(string) time.Duration
}

// WithResourceData same as common.WithGenClient, except it takes ResourceData instead of *schema.ResourceData
Expand Down
10 changes: 5 additions & 5 deletions internal/schemautil/schemautil.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

// OptionalStringPointer retrieves a string pointer to a field, empty string
// will be converted to nil
func OptionalStringPointer(d *schema.ResourceData, key string) *string {
func OptionalStringPointer(d ResourceData, key string) *string {
val, ok := d.GetOk(key)
if !ok {
return nil
Expand All @@ -30,7 +30,7 @@ func OptionalStringPointer(d *schema.ResourceData, key string) *string {
}

// OptionalIntPointer retrieves an int pointer to a field, if the field is not set, returns nil.
func OptionalIntPointer(d *schema.ResourceData, key string) *int {
func OptionalIntPointer(d ResourceData, key string) *int {
val, ok := d.GetOk(key)
if !ok {
return nil
Expand All @@ -43,7 +43,7 @@ func OptionalIntPointer(d *schema.ResourceData, key string) *int {
}

// OptionalBoolPointer retrieves a bool pointer to a field, if the field is not set, returns nil.
func OptionalBoolPointer(d *schema.ResourceData, key string) *bool {
func OptionalBoolPointer(d ResourceData, key string) *bool {
val, ok := d.GetOk(key)
if !ok {
return nil
Expand Down Expand Up @@ -256,7 +256,7 @@ func FlattenToString[T any](a []T) []string {
}

func CopyServiceUserPropertiesFromAPIResponseToTerraform(
d *schema.ResourceData,
d ResourceData,
user *aiven.ServiceUser,
projectName string,
serviceName string,
Expand Down Expand Up @@ -292,7 +292,7 @@ func CopyServiceUserPropertiesFromAPIResponseToTerraform(
}

func CopyServiceUserGenPropertiesFromAPIResponseToTerraform(
d *schema.ResourceData,
d ResourceData,
user *service.ServiceUserGetOut,
projectName string,
serviceName string,
Expand Down
10 changes: 5 additions & 5 deletions internal/schemautil/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ func ResourceServiceDelete(ctx context.Context, d *schema.ResourceData, m interf
}

func copyServicePropertiesFromAPIResponseToTerraform(
d *schema.ResourceData,
d ResourceData,
s *service.ServiceGetOut,
servicePlanParams PlanParameters,
project string,
Expand Down Expand Up @@ -844,7 +844,7 @@ func FlattenServiceComponents(r *service.ServiceGetOut) []map[string]interface{}
//
// We should change this in the next major version.
func copyConnectionInfoFromAPIResponseToTerraform(
d *schema.ResourceData,
d ResourceData,
serviceType string,
connectionInfo *service.ConnectionInfoOut,
metadata map[string]any,
Expand Down Expand Up @@ -1057,7 +1057,7 @@ func DatasourceServiceRead(ctx context.Context, d *schema.ResourceData, m interf
return diag.Errorf("common %s/%s not found", projectName, serviceName)
}

func getContactEmailListForAPI(d *schema.ResourceData) (*[]service.TechEmailIn, error) {
func getContactEmailListForAPI(d ResourceData) (*[]service.TechEmailIn, error) {
if valuesInterface, ok := d.GetOk("tech_emails"); ok {
var emails []service.TechEmailIn
err := Remarshal(valuesInterface.(*schema.Set).List(), &emails)
Expand All @@ -1069,11 +1069,11 @@ func getContactEmailListForAPI(d *schema.ResourceData) (*[]service.TechEmailIn,
return &[]service.TechEmailIn{}, nil
}

func ExpandService(name string, d *schema.ResourceData) (map[string]any, error) {
func ExpandService(name string, d ResourceData) (map[string]any, error) {
return converters.Expand(converters.ServiceUserConfig, name, d)
}

func FlattenService(name string, d *schema.ResourceData, dto map[string]any) error {
func FlattenService(name string, d ResourceData, dto map[string]any) error {
return converters.Flatten(converters.ServiceUserConfig, name, d, dto)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/schemautil/static_ips.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func ServiceStaticIpsList(ctx context.Context, client avngen.Client, projectName
}

// DiffStaticIps takes a service resource and computes which static ips to assign and which to disassociate
func DiffStaticIps(ctx context.Context, d *schema.ResourceData, client avngen.Client) (ass, dis []string, err error) {
func DiffStaticIps(ctx context.Context, d ResourceData, client avngen.Client) (ass, dis []string, err error) {
ipsFromSchema := FlattenToString(d.Get("static_ips").(*schema.Set).List())
ipsFromAPI, err := ServiceStaticIpsList(ctx, client, d.Get("project").(string), d.Get("service_name").(string))
if err != nil {
Expand Down
16 changes: 8 additions & 8 deletions internal/schemautil/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
aivenServicesStartingState = "WAITING_FOR_SERVICES"
)

func WaitForServiceCreation(ctx context.Context, d *schema.ResourceData, client avngen.Client) (*service.ServiceGetOut, error) {
func WaitForServiceCreation(ctx context.Context, d ResourceData, client avngen.Client) (*service.ServiceGetOut, error) {
projectName, serviceName := d.Get("project").(string), d.Get("service_name").(string)

timeout := d.Timeout(schema.TimeoutCreate)
Expand Down Expand Up @@ -80,7 +80,7 @@ func WaitForServiceCreation(ctx context.Context, d *schema.ResourceData, client
return aux.(*service.ServiceGetOut), nil
}

func WaitForServiceUpdate(ctx context.Context, d *schema.ResourceData, client avngen.Client) (*service.ServiceGetOut, error) {
func WaitForServiceUpdate(ctx context.Context, d ResourceData, client avngen.Client) (*service.ServiceGetOut, error) {
projectName, serviceName := d.Get("project").(string), d.Get("service_name").(string)

timeout := d.Timeout(schema.TimeoutUpdate)
Expand Down Expand Up @@ -129,7 +129,7 @@ func WaitForServiceUpdate(ctx context.Context, d *schema.ResourceData, client av
return aux.(*service.ServiceGetOut), nil
}

func WaitStaticIpsDissociation(ctx context.Context, d *schema.ResourceData, m interface{}) error {
func WaitStaticIpsDissociation(ctx context.Context, d ResourceData, m interface{}) error {
timeout := d.Timeout(schema.TimeoutDelete)
log.Printf("[DEBUG] Static Ip dissassociation timeout %.0f minutes", timeout.Minutes())

Expand Down Expand Up @@ -158,7 +158,7 @@ func WaitStaticIpsDissociation(ctx context.Context, d *schema.ResourceData, m in
return nil
}

func WaitForDeletion(ctx context.Context, d *schema.ResourceData, m interface{}) error {
func WaitForDeletion(ctx context.Context, d ResourceData, m interface{}) error {
client := m.(*aiven.Client)

projectName, serviceName := d.Get("project").(string), d.Get("service_name").(string)
Expand Down Expand Up @@ -272,7 +272,7 @@ func backupsReady(s *service.ServiceGetOut) bool {

// staticIpsReady checks that the static ips that are associated with the service are either
// in state 'assigned' or 'available'
func staticIpsReady(ctx context.Context, d *schema.ResourceData, client avngen.Client) (bool, error) {
func staticIpsReady(ctx context.Context, d ResourceData, client avngen.Client) (bool, error) {
resourceIPs := staticIpsForServiceFromSchema(d)
if len(resourceIPs) == 0 {
return true, nil
Expand Down Expand Up @@ -300,7 +300,7 @@ func staticIpsReady(ctx context.Context, d *schema.ResourceData, client avngen.C
// all static ips that were associated to the service are available again
func staticIpsDisassociatedAfterServiceDeletion(
ctx context.Context,
d *schema.ResourceData,
d ResourceData,
m interface{},
) (bool, error) {
expectedStaticIps := staticIpsForServiceFromSchema(d)
Expand Down Expand Up @@ -333,7 +333,7 @@ func staticIpsDisassociatedAfterServiceDeletion(

// staticIpsAreDisassociated checks that after service update
// all static ips that are not used by the service anymore are available again
func staticIpsAreDisassociated(ctx context.Context, d *schema.ResourceData, m interface{}) (bool, error) {
func staticIpsAreDisassociated(ctx context.Context, d ResourceData, m interface{}) (bool, error) {
client := m.(*aiven.Client)
projectName := d.Get("project").(string)
serviceName := d.Get("service_name").(string)
Expand All @@ -359,7 +359,7 @@ L:
return true, nil
}

func staticIpsForServiceFromSchema(d *schema.ResourceData) []string {
func staticIpsForServiceFromSchema(d ResourceData) []string {
return FlattenToString(d.Get("static_ips").(*schema.Set).List())
}

Expand Down
18 changes: 9 additions & 9 deletions internal/sdkprovider/userconfig/converters/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func SetUserConfig(kind userConfigType, name string, s map[string]*schema.Schema
s[userConfigKey(kind, name)] = userConfig
}

func Expand(kind userConfigType, name string, d *schema.ResourceData) (map[string]any, error) {
func Expand(kind userConfigType, name string, d resourceData) (map[string]any, error) {
m, err := expand(kind, name, d)
if err != nil {
return nil, fmt.Errorf("error converting user config options for %s type %q to API format: %w", kind, name, err)
Expand All @@ -97,7 +97,7 @@ func Expand(kind userConfigType, name string, d *schema.ResourceData) (map[strin

// expand expands schema.ResourceData into a DTO map.
// It takes schema.Schema to know how to turn a TF item into json.
func expand(kind userConfigType, name string, d *schema.ResourceData) (map[string]any, error) {
func expand(kind userConfigType, name string, d resourceData) (map[string]any, error) {
if getUserConfig(kind, name) == nil {
// does not have a user config for given kind and name
return nil, nil
Expand Down Expand Up @@ -143,11 +143,11 @@ func expand(kind userConfigType, name string, d *schema.ResourceData) (map[strin
// With the state it is possible to say "if value is null", hence if it is defined by user.
// With schema.ResourceData, you get the value that might be a zero-value.
type stateCompose struct {
key string // state attribute name or schema.ResourceData key
path string // schema.ResourceData path, i.e., foo.0.bar.0.baz to get the value
schema *schema.Schema // tf schema
config cty.Value // tf file state, it knows if resource value is null
resource *schema.ResourceData // tf resource that has both tf state and data that is received from the API
key string // state attribute name or schema.ResourceData key
path string // schema.ResourceData path, i.e., foo.0.bar.0.baz to get the value
schema *schema.Schema // tf schema
config cty.Value // tf file state, it knows if resource value is null
resource resourceData // tf resource that has both tf state and data that is received from the API
}

// listItems returns a list of object's states
Expand Down Expand Up @@ -343,7 +343,7 @@ func expandList(s *stateCompose) (any, error) {
return items, nil
}

func Flatten(kind userConfigType, name string, d *schema.ResourceData, dto map[string]any) error {
func Flatten(kind userConfigType, name string, d resourceData, dto map[string]any) error {
err := flatten(kind, name, d, dto)
if err != nil {
return fmt.Errorf("error converting user config options for %s type %q from API format: %w", kind, name, err)
Expand All @@ -352,7 +352,7 @@ func Flatten(kind userConfigType, name string, d *schema.ResourceData, dto map[s
}

// flatten flattens DTO into a terraform compatible object and sets value to the field
func flatten(kind userConfigType, name string, d *schema.ResourceData, dto map[string]any) error {
func flatten(kind userConfigType, name string, d resourceData, dto map[string]any) error {
if getUserConfig(kind, name) == nil {
// does not have a user config for given kind and name
return nil
Expand Down
8 changes: 7 additions & 1 deletion internal/sdkprovider/userconfig/converters/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"
"strings"

"github.com/hashicorp/go-cty/cty"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)
Expand All @@ -30,9 +31,14 @@ func renameAliasesToDto(kind userConfigType, name string, dto map[string]any) {
}
}

// resourceData to test schema.ResourceData with unit tests
// resourceData implements *schema.ResourceData
type resourceData interface {
Get(string) any
GetOk(string) (any, bool)
GetRawConfig() cty.Value
HasChange(string) bool
IsNewResource() bool
Set(string, any) error
}

// renameAliasesToTfo renames aliases to TF object
Expand Down
16 changes: 16 additions & 0 deletions internal/sdkprovider/userconfig/converters/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"regexp"
"testing"

"github.com/hashicorp/go-cty/cty"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -249,6 +250,21 @@ type resourceDataMock struct {
m map[string]any
}

func (r *resourceDataMock) Get(_ string) any {
panic("implement me")
}
func (r *resourceDataMock) GetRawConfig() cty.Value {
panic("implement me")
}
func (r *resourceDataMock) HasChange(_ string) bool {
panic("implement me")
}
func (r *resourceDataMock) IsNewResource() bool {
panic("implement me")
}
func (r *resourceDataMock) Set(_ string, _ any) error {
panic("implement me")
}
func (r *resourceDataMock) GetOk(k string) (any, bool) {
v, ok := r.m[k]
return v, ok
Expand Down
48 changes: 48 additions & 0 deletions mocks/resourcedata.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ad2f2ad

Please sign in to comment.