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

Feat: support regex in configuration variables #370

Merged
merged 2 commits into from
May 10, 2022
Merged
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
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
}