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

see commits #4

Open
wants to merge 4 commits into
base: master
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
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,12 @@
examples/.terraform*
examples/terraform.tfstate*
dist/*

# executable
terraform-provider-netskopebwan

# ignore dir
ignore

# Mac
.DS_Store
4 changes: 2 additions & 2 deletions bwan/data_source_gateway_bgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ type dataSourceGatewayBgpInput struct {

func dataSourceGatewayBgp() *schema.Resource {
swaggerSchema, binder, inputBinder := ReflectSchema(dataSourceGatewayBgpInput{}, Cfg{
"name": {Schema: schema.Schema{Required: true}},
"gateway_id": {Schema: schema.Schema{Required: true}},
"name": {Schema: &schema.Schema{Required: true}},
"gateway_id": {Schema: &schema.Schema{Required: true}},
})

rt := _dataSourceGatewayBgp{Binder: binder, InputBinder: inputBinder}
Expand Down
4 changes: 2 additions & 2 deletions bwan/data_source_gateway_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ type dataSourceGatewayInterfaceInput struct {

func dataSourceGatewayInterface() *schema.Resource {
swaggerSchema, binder, inputBinder := ReflectSchema(dataSourceGatewayInterfaceInput{}, Cfg{
"name": {Schema: schema.Schema{Required: true}},
"gateway_id": {Schema: schema.Schema{Required: true}},
"name": {Schema: &schema.Schema{Required: true}},
"gateway_id": {Schema: &schema.Schema{Required: true}},
})

rt := _dataSourceGatewayInterface{Binder: binder, InputBinder: inputBinder}
Expand Down
6 changes: 3 additions & 3 deletions bwan/data_source_gateway_nat.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type dataSourceGatewayNatInput struct {

func dataSourceGatewayNat() *schema.Resource {
swaggerSchema, binder, inputBinder := ReflectSchema(dataSourceGatewayNatInput{}, Cfg{
"gateway_id": {Schema: schema.Schema{Required: true}},
"gateway_id": {Schema: &schema.Schema{Required: true}},
})

rt := _dataSourceGatewayNat{Binder: binder,
Expand All @@ -86,8 +86,8 @@ func dataSourceGatewayNat() *schema.Resource {

func dataSourceGatewayPortForward() *schema.Resource {
swaggerSchema, binder, inputBinder := ReflectSchema(dataSourceGatewayNatInput{}, Cfg{
"gateway_id": {Schema: schema.Schema{Required: true}},
"name": {Schema: schema.Schema{Required: true}},
"gateway_id": {Schema: &schema.Schema{Required: true}},
"name": {Schema: &schema.Schema{Required: true}},
})

rt := _dataSourceGatewayNat{Binder: binder,
Expand Down
2 changes: 1 addition & 1 deletion bwan/data_source_gateway_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type _dataSourcePolicy struct {

func dataSourcePolicy() *schema.Resource {
swaggerSchema, binder, swaggerInputBinder := ReflectSchema(swagger.Policy{}, Cfg{
"name": {Schema: schema.Schema{Required: true}},
"name": {Schema: &schema.Schema{Required: true}},
})

rt := _dataSourcePolicy{Binder: binder, InputBinder: swaggerInputBinder}
Expand Down
4 changes: 2 additions & 2 deletions bwan/data_source_gateway_staticroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ type dataSourceGatewayStaticRouteInput struct {
func dataSourceGatewayStaticRoute() *schema.Resource {
swaggerSchema, binder, inputBinder := ReflectSchema(
dataSourceGatewayStaticRouteInput{}, Cfg{
"gateway_id": {Schema: schema.Schema{Required: true}},
"destination": {Schema: schema.Schema{Required: true}},
"gateway_id": {Schema: &schema.Schema{Required: true}},
"destination": {Schema: &schema.Schema{Required: true}},
})

rt := _dataSourceGatewayStaticRoute{Binder: binder, InputBinder: inputBinder}
Expand Down
2 changes: 1 addition & 1 deletion bwan/data_source_tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type _dataSourceTenant struct {
func dataSourceTenant() *schema.Resource {
swaggerSchema, binder, swaggerInputBinder := ReflectSchema(swagger.Tenant{}, Cfg{
"name": {
Schema: schema.Schema{
Schema: &schema.Schema{
Optional: true,
},
},
Expand Down
2 changes: 1 addition & 1 deletion bwan/data_source_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type _dataSourceUser struct {
func dataSourceUser() *schema.Resource {
swaggerSchema, binder, swaggerInputBinder := ReflectSchema(swagger.User{}, Cfg{
"name": {
Schema: schema.Schema{
Schema: &schema.Schema{
Optional: true,
},
},
Expand Down
73 changes: 62 additions & 11 deletions bwan/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,20 @@ func ToSnakeCase(field reflect.StructField) string {
}

type Cfg map[string]struct {
schema.Schema
*schema.Schema
EmptyIsNotNull bool
}

func (c Cfg) EmptyIsNotNull(k string) bool {
if c[k].EmptyIsNotNull {
return true
}

if c["*"].EmptyIsNotNull {
return true
}

return false
}

func ReflectSchema(v interface{}, cfg Cfg) (map[string]*schema.Schema, []FieldBinder, []FieldBinder) {
Expand Down Expand Up @@ -84,18 +97,28 @@ func applyBinderInput(t reflect.Type, bm []FieldBinder, get func(k string) (inte
panic(fmt.Sprintf("field %v does not exist on %v", b.FieldName, nv.Type().String()))
}

mv, ok := get(b.MapKey)
if !ok || mv == nil {
continue
mv, _ := get(b.MapKey)
var mvv reflect.Value
if mv == nil {
switch f.Type().Kind() {
case reflect.Array, reflect.Slice:
mvv = reflect.Zero(f.Type())
default:
continue
}
} else {
mvv = reflect.ValueOf(mv)
}

iv, err := b.Func(reflect.ValueOf(mv))
iv, err := b.Func(mvv)
if err != nil {
return reflect.Value{}, err
}

if iv == nil {
continue
}

v := reflect.ValueOf(iv)

switch f.Type().Kind() {
Expand Down Expand Up @@ -221,21 +244,31 @@ func reflectSchemaType(path string, t reflect.Type, cfg Cfg) (map[string]*schema
}

func reflectSchemaField(path string, cfg Cfg, t reflect.Type, extra, allowDirectObject bool) (*schema.Schema, BinderFunc, BinderFunc) {
fcfg, ok := cfg[path]
fcfg := cfg[path]

s := fcfg.Schema
hasSchema := s != nil
if s == nil {
s = &schema.Schema{}
}

var b, ib BinderFunc
s.Type, s.Elem, b, ib = reflectSchemaFieldType(path, t, cfg, allowDirectObject)
if extra && !ok {
if extra && !hasSchema {
s.Optional = true
s.Computed = true
if s.Type != schema.TypeList {
s.Computed = true
}
if path == "id" {
s.Computed = true
}
}

if s.Type == schema.TypeSet {
s.MaxItems = 1
}

return &s, b, ib
return s, b, ib
}

type BinderFunc func(v reflect.Value) (interface{}, error)
Expand Down Expand Up @@ -306,6 +339,10 @@ func reflectSchemaFieldType(path string, t reflect.Type, cfg Cfg, allowDirectObj

return schema.TypeList, innerType, func(v reflect.Value) (interface{}, error) {
if v.IsZero() {
v = reflect.MakeSlice(t, 0, 0)
}

if v.Len() == 0 && !cfg.EmptyIsNotNull(path) {
return nil, nil
}

Expand All @@ -321,11 +358,17 @@ func reflectSchemaFieldType(path string, t reflect.Type, cfg Cfg, allowDirectObj

return a, nil
}, func(v reflect.Value) (interface{}, error) {
if v.IsZero() {
var av []interface{}
if !v.IsZero() {
av = v.Interface().([]interface{})
} else {
av = []interface{}{}
}

if len(av) == 0 && !cfg.EmptyIsNotNull(path) {
return nil, nil
}

av := v.Interface().([]interface{})
nv := reflect.MakeSlice(t, 0, len(av))

for _, iv := range av {
Expand All @@ -344,10 +387,18 @@ func reflectSchemaFieldType(path string, t reflect.Type, cfg Cfg, allowDirectObj
return schema.TypeString, nil, func(v reflect.Value) (interface{}, error) {
t := v.Interface().(time.Time)

if t.IsZero() {
return nil, nil
}

return t.Format(time.RFC3339), nil
}, func(v reflect.Value) (interface{}, error) {
sv := v.Interface().(string)

if sv == "" {
return time.Time{}, nil
}

return time.Parse(time.RFC3339, sv)
}
}
Expand Down
65 changes: 56 additions & 9 deletions bwan/reflect_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package bwan

import (
"reflect"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"reflect"
"testing"
)

type Sanity struct {
Expand Down Expand Up @@ -61,7 +63,7 @@ var ArrayObjectSchema = ms{
Type: schema.TypeList,
Elem: &schema.Resource{Schema: NestedObjectChildSchema},
Optional: true,
Computed: true,
Computed: false,
},
}

Expand All @@ -86,7 +88,7 @@ var ArrayPrimitiveSchema = ms{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Computed: true,
Computed: false,
},
}

Expand Down Expand Up @@ -122,6 +124,10 @@ var EmbedObjectSchema = ms{
},
}

type ComplexObject struct {
Sub []ArrayObject
}

type i = interface{}
type m = map[string]i
type ms = map[string]*schema.Schema
Expand Down Expand Up @@ -152,7 +158,7 @@ func TestRoundTrip(t *testing.T) {
tests := []struct {
name string
t interface{}
out interface{}
out map[string]interface{}
}{
{"sanity", Sanity{}, m{
"string": "",
Expand Down Expand Up @@ -212,11 +218,16 @@ func TestRoundTrip(t *testing.T) {
}, m{
"strings": nil,
}},
{"array primitives", ArrayPrimitive{
{"array primitives EINN", ArrayPrimitive{
Strings: []string{},
}, m{
"strings": []i{},
}},
{"array primitives NOTSYMMETRICAL", ArrayPrimitive{
Strings: []string{},
}, m{
"strings": nil,
}},
{"array primitives", ArrayPrimitive{
Strings: []string{""},
}, m{
Expand All @@ -237,7 +248,12 @@ func TestRoundTrip(t *testing.T) {
}, m{
"children": nil,
}},
{"array object", ArrayObject{
{"array object NOTSYMMETRICAL", ArrayObject{
Children: []NestedObjectChild{},
}, m{
"children": nil,
}},
{"array object EINN", ArrayObject{
Children: []NestedObjectChild{},
}, m{
"children": []i{},
Expand Down Expand Up @@ -281,10 +297,36 @@ func TestRoundTrip(t *testing.T) {
"parent_id": "",
"id": "id",
}},
{"complex nesting", ComplexObject{
Sub: []ArrayObject{
{
Children: []NestedObjectChild{{Id: "1"}, {Id: "2"}},
},
{
Children: nil,
},
},
}, m{"sub": []i{
m{"children": []i{m{"id": "1"}, m{"id": "2"}}},
m{"children": nil},
}}},
}
for _, test := range tests {
test := test

t.Run(test.name, func(t *testing.T) {
_, bm, ibm := ReflectSchema(test.t, Cfg{})
t.Parallel()

isEmptyNotNil := strings.Contains(test.name, "EINN")
isNotSymetrical := strings.Contains(test.name, "NOTSYMMETRICAL")

cfg := Cfg{}
if isEmptyNotNil {
c := cfg["*"]
c.EmptyIsNotNull = true
cfg["*"] = c
}
_, bm, ibm := ReflectSchema(test.t, cfg)

ma, err := ApplyBinderMap(bm, reflect.ValueOf(test.t))
require.NoError(t, err)
Expand All @@ -297,7 +339,12 @@ func TestRoundTrip(t *testing.T) {
})
require.NoError(t, err)

assert.Equal(t, test.t, v.Interface())
if isNotSymetrical {
return
}

av := v.Interface()
assert.EqualValues(t, test.t, av)
})
}
}
Loading