Skip to content

Commit

Permalink
Feat: support regex in configuration variables (#370)
Browse files Browse the repository at this point in the history
support regex in configuration variables
  • Loading branch information
chpl committed May 10, 2022
1 parent ca47ac8 commit caf4157
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 1 deletion.
4 changes: 4 additions & 0 deletions client/configuration_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type ConfigurationVariable struct {
ToDelete *bool `json:"toDelete,omitempty"`
IsReadonly *bool `json:"isReadonly,omitempty"`
IsRequired *bool `json:"isRequired,omitempty"`
Regex string `json:"regex,omitempty"`
}

type ConfigurationVariableCreateParams struct {
Expand All @@ -58,6 +59,7 @@ type ConfigurationVariableCreateParams struct {
Format Format
IsReadonly bool
IsRequired bool
Regex string
}

type ConfigurationVariableUpdateParams struct {
Expand Down Expand Up @@ -134,6 +136,7 @@ func (client *ApiClient) ConfigurationVariableCreate(params ConfigurationVariabl
"organizationId": organizationId,
"isRequired": params.IsRequired,
"isReadonly": params.IsReadonly,
"regex": params.Regex,
}
if params.Scope != ScopeGlobal {
request["scopeId"] = params.ScopeId
Expand Down Expand Up @@ -187,6 +190,7 @@ func (client *ApiClient) ConfigurationVariableUpdate(updateParams ConfigurationV
"organizationId": organizationId,
"isRequired": commonParams.IsRequired,
"isReadonly": commonParams.IsReadonly,
"regex": commonParams.Regex,
}
if commonParams.Scope != ScopeGlobal {
request["scopeId"] = commonParams.ScopeId
Expand Down
6 changes: 6 additions & 0 deletions client/configuration_variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var _ = Describe("Configuration Variable", func() {
Schema: &schema,
IsReadonly: &isReadonly,
IsRequired: &isRequired,
Regex: "regex",
}

mockGlobalConfigurationVariable := ConfigurationVariable{
Expand All @@ -51,6 +52,7 @@ var _ = Describe("Configuration Variable", func() {
Schema: &schema,
IsReadonly: &isReadonly,
IsRequired: &isRequired,
Regex: "regex",
}

Describe("ConfigurationVariable", func() {
Expand Down Expand Up @@ -123,6 +125,7 @@ var _ = Describe("Configuration Variable", func() {
"schema": schema,
"isReadonly": *mockConfig.IsReadonly,
"isRequired": *mockConfig.IsRequired,
"regex": mockConfig.Regex,
}}
return request
}
Expand Down Expand Up @@ -150,6 +153,7 @@ var _ = Describe("Configuration Variable", func() {
Format: mockConfig.Schema.Format,
IsReadonly: *mockConfig.IsReadonly,
IsRequired: *mockConfig.IsRequired,
Regex: mockConfig.Regex,
},
)
}
Expand Down Expand Up @@ -225,6 +229,7 @@ var _ = Describe("Configuration Variable", func() {
},
"isReadonly": *mockConfigurationVariable.IsReadonly,
"isRequired": *mockConfigurationVariable.IsRequired,
"regex": mockConfigurationVariable.Regex,
}}

httpCall = mockHttpClient.EXPECT().
Expand All @@ -247,6 +252,7 @@ var _ = Describe("Configuration Variable", func() {
EnumValues: nil,
IsReadonly: *mockConfigurationVariable.IsReadonly,
IsRequired: *mockConfigurationVariable.IsRequired,
Regex: mockConfigurationVariable.Regex,
},
},
)
Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/configuration_variable.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ output "aws_default_region" {
- **is_required** (Boolean) specifies if the value of this variable must be set by lower scopes
- **name** (String) the name of the configuration variable
- **project_id** (String) search for the variable under this project, not globally
- **regex** (String) specifies a regular expression to validate variable value in UI
- **template_id** (String) search for the variable under this template, not globally
- **type** (String) 'terraform' or 'environment'. If specified as an argument, limits searching by variable name only to variables of this type.

Expand Down
1 change: 1 addition & 0 deletions docs/resources/configuration_variable.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ resource "env0_configuration_variable" "json_variable" {
- **is_required** (Boolean) the value of this variable must be set by lower scopes
- **is_sensitive** (Boolean) is the variable sensitive, defaults to false
- **project_id** (String) create the variable under this project, not globally
- **regex** (String) the value of this variable must match provided regular expression
- **template_id** (String) create the variable under this template, not globally
- **type** (String) default 'environment'. set to 'terraform' to create a terraform variable
- **value** (String, Sensitive) value for the configuration variable
Expand Down
7 changes: 7 additions & 0 deletions env0/data_configuration_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ func dataConfigurationVariable() *schema.Resource {
Computed: true,
Optional: true,
},
"regex": {
Type: schema.TypeString,
Description: "specifies a regular expression to validate variable value (enforced only in env0 UI)",
Computed: true,
Optional: true,
},
},
}
}
Expand Down Expand Up @@ -146,6 +152,7 @@ func dataConfigurationVariableRead(ctx context.Context, d *schema.ResourceData,
d.Set("enum", variable.Schema.Enum)
d.Set("is_read_only", variable.IsReadonly)
d.Set("is_required", variable.IsRequired)
d.Set("regex", variable.Regex)

if variable.Schema.Format != client.Text {
d.Set("format", string(variable.Schema.Format))
Expand Down
2 changes: 2 additions & 0 deletions env0/data_configuration_variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func TestUnitConfigurationVariableData(t *testing.T) {
Schema: &client.ConfigurationVariableSchema{Type: "string", Format: client.HCL},
IsReadonly: &isReadonly,
IsRequired: &isRequired,
Regex: "regex",
}

checkResources := resource.ComposeAggregateTestCheckFunc(
Expand All @@ -46,6 +47,7 @@ func TestUnitConfigurationVariableData(t *testing.T) {
resource.TestCheckResourceAttr(accessor, "format", string(configurationVariable.Schema.Format)),
resource.TestCheckResourceAttr(accessor, "is_read_only", strconv.FormatBool(*configurationVariable.IsReadonly)),
resource.TestCheckResourceAttr(accessor, "is_required", strconv.FormatBool(*configurationVariable.IsRequired)),
resource.TestCheckResourceAttr(accessor, "regex", "regex"),
)

t.Run("ScopeGlobal", func(t *testing.T) {
Expand Down
10 changes: 10 additions & 0 deletions env0/resource_configuration_variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ func resourceConfigurationVariable() *schema.Resource {
Default: false,
ConflictsWith: []string{"environment_id"},
},
"regex": {
Type: schema.TypeString,
Description: "the value of this variable must match provided regular expression (enforced only in env0 UI)",
Optional: true,
},
},
}
}
Expand Down Expand Up @@ -148,6 +153,7 @@ func resourceConfigurationVariableCreate(ctx context.Context, d *schema.Resource
format := client.Format(d.Get("format").(string))
isReadOnly := d.Get("is_read_only").(bool)
isRequired := d.Get("is_required").(bool)
regex := d.Get("regex").(string)

if err := validateNilValue(isReadOnly, isRequired, value); err != nil {
return diag.Errorf(err.Error())
Expand Down Expand Up @@ -179,6 +185,7 @@ func resourceConfigurationVariableCreate(ctx context.Context, d *schema.Resource
Format: format,
IsReadonly: isReadOnly,
IsRequired: isRequired,
Regex: regex,
})
if err != nil {
return diag.Errorf("could not create configurationVariable: %v", err)
Expand Down Expand Up @@ -227,6 +234,7 @@ func resourceConfigurationVariableRead(ctx context.Context, d *schema.ResourceDa
d.Set("is_sensitive", variable.IsSensitive)
d.Set("is_read_only", variable.IsReadonly)
d.Set("is_required", variable.IsRequired)
d.Set("regex", variable.Regex)
if variable.Type != nil && *variable.Type == client.ConfigurationVariableTypeTerraform {
d.Set("type", "terraform")
} else {
Expand Down Expand Up @@ -258,6 +266,7 @@ func resourceConfigurationVariableUpdate(ctx context.Context, d *schema.Resource
format := client.Format(d.Get("format").(string))
isReadOnly := d.Get("is_read_only").(bool)
isRequired := d.Get("is_required").(bool)
regex := d.Get("regex").(string)

if err := validateNilValue(isReadOnly, isRequired, value); err != nil {
return diag.Errorf(err.Error())
Expand Down Expand Up @@ -288,6 +297,7 @@ func resourceConfigurationVariableUpdate(ctx context.Context, d *schema.Resource
Format: format,
IsReadonly: isReadOnly,
IsRequired: isRequired,
Regex: regex,
}})
if err != nil {
return diag.Errorf("could not update configurationVariable: %v", err)
Expand Down
55 changes: 55 additions & 0 deletions env0/resource_configuration_variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,61 @@ resource "{{.resourceType}}" "{{.projResourceName}}" {
})
})

t.Run("Create and update with regex", func(t *testing.T) {
initialVar := client.ConfigurationVariable{
Id: "regex-var-id",
Name: "regex-var-name",
Regex: "initial-regex",
}
initialResource := resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"name": initialVar.Name,
"regex": initialVar.Regex,
})
createParams := client.ConfigurationVariableCreateParams{
Name: initialVar.Name,
Regex: initialVar.Regex,
Scope: client.ScopeGlobal,
}

updatedVar := initialVar
updatedVar.Regex = "updated-regex"
updatedResource := resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"name": updatedVar.Name,
"regex": updatedVar.Regex,
})
updateParams := createParams
updateParams.Regex = updatedVar.Regex

steps := resource.TestCase{
Steps: []resource.TestStep{
{
Config: initialResource,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "name", initialVar.Name),
resource.TestCheckResourceAttr(accessor, "regex", initialVar.Regex),
),
},
{
Config: updatedResource,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "name", updatedVar.Name),
resource.TestCheckResourceAttr(accessor, "regex", updatedVar.Regex),
),
},
},
}

runUnitTest(t, steps, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().ConfigurationVariableCreate(createParams).Times(1).Return(initialVar, nil),
mock.EXPECT().ConfigurationVariablesById(initialVar.Id).Times(2).Return(initialVar, nil),
mock.EXPECT().ConfigurationVariableUpdate(client.ConfigurationVariableUpdateParams{CommonParams: updateParams, Id: updatedVar.Id}).Times(1).Return(updatedVar, nil),
mock.EXPECT().ConfigurationVariablesById(initialVar.Id).Times(1).Return(updatedVar, nil),
mock.EXPECT().ConfigurationVariableDelete(initialVar.Id).Times(1).Return(nil),
)
})
})

t.Run("Create Enum", func(t *testing.T) {
schema := client.ConfigurationVariableSchema{
Type: "string",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"tested1_value": "fake value 1 after update",
"tested3_enum_1": "First",
"tested3_enum_2": "Second"
"tested3_enum_2": "Second",
"regex" : "^test-\\d+$"
}
16 changes: 16 additions & 0 deletions tests/integration/003_configuration_variable/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,19 @@ output "tested3_enum_2" {
value = data.env0_configuration_variable.tested3.enum[1]
sensitive = true
}

resource "env0_configuration_variable" "regex_var" {
project_id = env0_project.test_project.id
name = "regex_var"
regex = "^test-\\d+$"
}

data "env0_configuration_variable" "regex_var" {
project_id = env0_project.test_project.id
name = "regex_var"
depends_on = [env0_configuration_variable.regex_var]
}

output "regex" {
value = data.env0_configuration_variable.regex_var.regex
}

0 comments on commit caf4157

Please sign in to comment.