diff --git a/go.mod b/go.mod index 12053ac..7d9cfbc 100644 --- a/go.mod +++ b/go.mod @@ -50,3 +50,5 @@ require ( google.golang.org/genproto v0.0.0-20180718234121-fedd2861243f google.golang.org/grpc v1.13.0 ) + +go 1.13 diff --git a/launchdarkly/protocol.go b/launchdarkly/protocol.go index 7ec3061..19e934a 100644 --- a/launchdarkly/protocol.go +++ b/launchdarkly/protocol.go @@ -14,6 +14,12 @@ type JsonProject struct { Environments []JsonEnvironment `json:"environments"` } +type JsonVariations struct { + Value interface{} `json:"value"` + Name string `json:"name"` + Description string `json:"description"` +} + type JsonCustomProperty struct { Name string `json:"name"` Value []string `json:"value"` @@ -25,6 +31,7 @@ type JsonFeatureFlag struct { Description string `json:"description"` Temporary bool `json:"temporary"` IncludeInSnippet bool `json:"includeInSnippet"` + Variations []JsonVariations `json:"variations"` Tags []string `json:"tags"` CustomProperties map[string]JsonCustomProperty `json:"customProperties"` } diff --git a/launchdarkly/resource_feature_flag.go b/launchdarkly/resource_feature_flag.go index 99faf7b..0648f9a 100644 --- a/launchdarkly/resource_feature_flag.go +++ b/launchdarkly/resource_feature_flag.go @@ -1,9 +1,19 @@ package launchdarkly import ( + "strconv" + "github.com/hashicorp/terraform/helper/schema" ) +const VARIATION_NAME_KEY = "name" +const VARIATION_DESCRIPTION_KEY = "description" +const VARIATION_VALUE_KEY = "value" +const VARIATIONS_STRING_KIND = "string" +const VARIATIONS_NUMBER_KIND = "number" +const VARIATIONS_BOOLEAN_KIND = "boolean" +const DEFAULT_VARIATIONS_KIND = VARIATIONS_BOOLEAN_KIND + func resourceFeatureFlag() *schema.Resource { return &schema.Resource{ Create: resourceFeatureFlagCreate, @@ -43,6 +53,36 @@ func resourceFeatureFlag() *schema.Resource { Optional: true, Default: false, }, + "variations_kind": { + Type: schema.TypeString, + Optional: true, + Default: DEFAULT_VARIATIONS_KIND, + ValidateFunc: validateFeatureFlagVariationsType, + ForceNew: true, + }, + "variations": { + Type: schema.TypeList, + Optional: true, + MinItems: 2, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateVariationValue, + }, + "name": { + Type: schema.TypeString, + Optional: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, "tags": { Type: schema.TypeList, Optional: true, @@ -87,8 +127,15 @@ func resourceFeatureFlagCreate(d *schema.ResourceData, m interface{}) error { temporary := d.Get("temporary").(bool) includeInSnippet := d.Get("include_in_snippet").(bool) tags := d.Get("tags").([]interface{}) + variationsKind := d.Get("variations_kind").(string) + variations := d.Get("variations").([]interface{}) customProperties := d.Get("custom_properties").([]interface{}) + transformedVariations, err := transformVariationsFromTerraformFormat(variations, variationsKind) + if err != nil { + return err + } + transformedCustomProperties, err := transformCustomPropertiesFromTerraformFormat(customProperties) if err != nil { return err @@ -101,6 +148,7 @@ func resourceFeatureFlagCreate(d *schema.ResourceData, m interface{}) error { Temporary: temporary, IncludeInSnippet: includeInSnippet, Tags: transformTagsFromTerraformFormat(tags), + Variations: transformedVariations, CustomProperties: transformedCustomProperties, } @@ -117,6 +165,7 @@ func resourceFeatureFlagCreate(d *schema.ResourceData, m interface{}) error { d.Set("temporary", temporary) d.Set("include_in_snippet", includeInSnippet) d.Set("tags", tags) + d.Set("variations", variations) d.Set("custom_properties", customProperties) return nil @@ -222,6 +271,40 @@ func transformTagsFromTerraformFormat(tags []interface{}) []string { return transformed } +func transformVariationsFromTerraformFormat(variations []interface{}, variationsKind string) ([]JsonVariations, error) { + transformedVariations := make([]JsonVariations, len(variations)) + for index, rawVariationValue := range variations { + variation := rawVariationValue.(map[string]interface{}) + var value interface{} + name := variation[VARIATION_NAME_KEY].(string) + description := variation[VARIATION_DESCRIPTION_KEY].(string) + + if variationsKind == VARIATIONS_STRING_KIND { + value = variation[VARIATION_VALUE_KEY].(string) + } else if variationsKind == VARIATIONS_NUMBER_KIND { + convertedNumberValue, err := strconv.Atoi(variation[VARIATION_VALUE_KEY].(string)) + if err != nil { + return nil, err + } + value = convertedNumberValue + } else if variationsKind == VARIATIONS_BOOLEAN_KIND { + convertedBooleanValue, err := strconv.ParseBool(variation[VARIATION_VALUE_KEY].(string)) + if err != nil { + return nil, err + } + value = convertedBooleanValue + } + + transformedVariations[index] = JsonVariations{ + Name: name, + Value: value, + Description: description, + } + } + + return transformedVariations, nil +} + func transformCustomPropertiesFromTerraformFormat(properties []interface{}) (map[string]JsonCustomProperty, error) { transformed := make(map[string]JsonCustomProperty) diff --git a/launchdarkly/validations.go b/launchdarkly/validations.go index 4129f06..61801b9 100644 --- a/launchdarkly/validations.go +++ b/launchdarkly/validations.go @@ -6,6 +6,8 @@ import ( "regexp" ) +var supportedVariationsType = [3]string{VARIATIONS_NUMBER_KIND, VARIATIONS_STRING_KIND, VARIATIONS_BOOLEAN_KIND} + func validateKey(v interface{}, k string) ([]string, []error) { value := v.(string) @@ -42,6 +44,36 @@ func validateFeatureFlagKey(v interface{}, k string) ([]string, []error) { return nil, nil } +func validateFeatureFlagVariationsType(v interface{}, k string) ([]string, []error) { + value, ok := v.(string) + + if !ok { + return nil, []error{errors.New(fmt.Sprintf("expected %s to be a string", k))} + } + + for _, validVariationsType := range supportedVariationsType { + if value == validVariationsType { + return nil, nil + } + } + + return nil, []error{errors.New(fmt.Sprintf("expected %s to be one of %v, got %s", k, []string{"number", "boolean", "string"}, value))} +} + +func validateVariationValue(v interface{}, k string) ([]string, []error) { + value, ok := v.(string) + + if !ok { + return nil, []error{errors.New(fmt.Sprintf("expected %s to be a string", k))} + } + + if len(value) < 1 { + return nil, []error{errors.New(fmt.Sprintf("%s cannot be an empty string", k))} + } + + return nil, nil +} + func validateColor(v interface{}, k string) ([]string, []error) { value := v.(string)