forked from launchdarkly/terraform-provider-launchdarkly
/
clause_helper.go
167 lines (155 loc) · 4.63 KB
/
clause_helper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package launchdarkly
import (
"fmt"
"reflect"
"strconv"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
ldapi "github.com/launchdarkly/api-client-go/v7"
)
const (
BOOL_CLAUSE_VALUE = "boolean"
STRING_CLAUSE_VALUE = "string"
NUMBER_CLAUSE_VALUE = "number"
)
func clauseSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
Description: "List of nested blocks specifying the logical clauses to evaluate",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
ATTRIBUTE: {
Type: schema.TypeString,
Required: true,
Description: "The user attribute to operate on",
},
OP: {
Type: schema.TypeString,
Required: true,
Description: "The operator associated with the rule clause. Available options are in, endsWith, startsWith, matches, contains, lessThan, lessThanOrEqual, greaterThanOrEqual, before, after, segmentMatch, semVerEqual, semVerLessThan, and semVerGreaterThan",
ValidateDiagFunc: validateOp(),
},
VALUES: {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Required: true,
Description: "The list of values associated with the rule clause",
},
VALUE_TYPE: {
Type: schema.TypeString,
Default: STRING_CLAUSE_VALUE,
Optional: true,
Description: "The type for each of the clause's values. Available types are boolean, string, and number. If omitted, value_type defaults to string",
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(
[]string{
BOOL_CLAUSE_VALUE,
STRING_CLAUSE_VALUE,
NUMBER_CLAUSE_VALUE,
},
false,
)),
},
NEGATE: {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Whether to negate the rule clause",
},
},
},
}
}
func clauseFromResourceData(val interface{}) (ldapi.Clause, error) {
clauseMap := val.(map[string]interface{})
c := ldapi.Clause{
Attribute: clauseMap[ATTRIBUTE].(string),
Op: clauseMap[OP].(string),
Negate: clauseMap[NEGATE].(bool),
}
valueType := clauseMap[VALUE_TYPE].(string)
values, err := clauseValuesFromResourceData(clauseMap[VALUES].([]interface{}), valueType)
if err != nil {
return c, err
}
c.Values = values
return c, nil
}
func clauseValuesFromResourceData(schemaValues []interface{}, valueType string) ([]interface{}, error) {
typedValues := make([]interface{}, 0, len(schemaValues))
for _, schemaValue := range schemaValues {
strValue, ok := schemaValue.(string)
if !ok {
return nil, fmt.Errorf("invalid clause value: %v", schemaValue)
}
v, err := clauseValueFromResourceData(strValue, valueType)
if err != nil {
return nil, err
}
typedValues = append(typedValues, v)
}
return typedValues, nil
}
func clauseValueFromResourceData(strValue string, valueType string) (interface{}, error) {
switch valueType {
case STRING_CLAUSE_VALUE:
return strValue, nil
case BOOL_CLAUSE_VALUE:
return convertBoolStringToBool(strValue)
case NUMBER_CLAUSE_VALUE:
return convertNumberStringToFloat(strValue)
}
return nil, fmt.Errorf("invalid clause value type %q", valueType)
}
func convertBoolStringToBool(boolStr string) (bool, error) {
switch boolStr {
case "true":
return true, nil
case "false":
return false, nil
}
return false, fmt.Errorf("invalid boolean string %q", boolStr)
}
func convertNumberStringToFloat(numStr string) (float64, error) {
num, err := strconv.ParseFloat(numStr, 64)
if err != nil {
return num, fmt.Errorf("invalid number string %q", numStr)
}
return num, nil
}
func clausesToResourceData(clauses []ldapi.Clause) (interface{}, error) {
transformed := make([]interface{}, len(clauses))
for i, c := range clauses {
var err error
var valueType string
strValues := make([]interface{}, 0, len(c.Values))
for _, v := range c.Values {
valueType, err = inferClauseValueTypeFromValue(v)
if err != nil {
return transformed, err
}
strValues = append(strValues, stringifyValue(v))
}
transformed[i] = map[string]interface{}{
ATTRIBUTE: c.Attribute,
OP: c.Op,
VALUES: strValues,
VALUE_TYPE: valueType,
NEGATE: c.Negate,
}
}
return transformed, nil
}
func inferClauseValueTypeFromValue(value interface{}) (string, error) {
switch value.(type) {
case bool:
return BOOL_CLAUSE_VALUE, nil
case string:
return STRING_CLAUSE_VALUE, nil
case float64:
return NUMBER_CLAUSE_VALUE, nil
}
return "", fmt.Errorf("unknown clause value type: %q", reflect.TypeOf(value))
}