Skip to content

Commit

Permalink
Feat: add data source for env0_variable_set (#897)
Browse files Browse the repository at this point in the history
* Feat: add data source for env0_variable_set

* updated API call

* to lower case
  • Loading branch information
TomerHeber authored Jul 30, 2024
1 parent e7b53a1 commit bc149f5
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 2 deletions.
1 change: 1 addition & 0 deletions client/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ type ApiClientInterface interface {
ConfigurationSetCreate(payload *CreateConfigurationSetPayload) (*ConfigurationSet, error)
ConfigurationSetUpdate(id string, payload *UpdateConfigurationSetPayload) (*ConfigurationSet, error)
ConfigurationSet(id string) (*ConfigurationSet, error)
ConfigurationSets(scope string, scopeId string) ([]ConfigurationSet, error)
ConfigurationSetDelete(id string) error
ConfigurationVariablesBySetId(setId string) ([]ConfigurationVariable, error)
AssignConfigurationSets(scope string, scopeId string, sets []string) error
Expand Down
15 changes: 15 additions & 0 deletions client/api_client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 20 additions & 1 deletion client/configuration_set.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package client

import "fmt"
import (
"fmt"
"strings"
)

type CreateConfigurationSetPayload struct {
Name string `json:"name"`
Expand All @@ -22,6 +25,7 @@ type ConfigurationSet struct {
Name string `json:"name"`
Description string `json:"description"`
AssignmentScope string `json:"assignmentScope"`
CreationScopeId string `json:"creationScopeId"`
}

func (client *ApiClient) ConfigurationSetCreate(payload *CreateConfigurationSetPayload) (*ConfigurationSet, error) {
Expand Down Expand Up @@ -62,6 +66,21 @@ func (client *ApiClient) ConfigurationSet(id string) (*ConfigurationSet, error)
return &result, nil
}

func (client *ApiClient) ConfigurationSets(scope string, scopeId string) ([]ConfigurationSet, error) {
var result []ConfigurationSet

params := map[string]string{
"scope": strings.ToLower(scope),
"scopeId": scopeId,
}

if err := client.http.Get("/configuration-sets", params, &result); err != nil {
return nil, err
}

return result, nil
}

func (client *ApiClient) ConfigurationSetDelete(id string) error {
return client.http.Delete("/configuration-sets/"+id, nil)
}
Expand Down
58 changes: 58 additions & 0 deletions client/configuration_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,62 @@ var _ = Describe("Configuration Set", func() {
Expect(variables).To(Equal(mockVariables))
})
})

Describe("get configuration variables by set project id", func() {
mockVariables := []ConfigurationSet{
{
Id: "id",
Name: "name",
CreationScopeId: "create_scope_id",
},
}

var variables []ConfigurationSet

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Get("/configuration-sets", map[string]string{
"scopeId": mockVariables[0].CreationScopeId,
"scope": "project",
}, gomock.Any()).
Do(func(path string, request interface{}, response *[]ConfigurationSet) {
*response = mockVariables
}).Times(1)

variables, _ = apiClient.ConfigurationSets("PROJECT", mockVariables[0].CreationScopeId)
})

It("Should return configuration sets", func() {
Expect(variables).To(Equal(mockVariables))
})
})

Describe("get configuration variables by set organization id", func() {
mockVariables := []ConfigurationSet{
{
Id: "id",
Name: "name",
CreationScopeId: "create_scope_id",
},
}

var variables []ConfigurationSet

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Get("/configuration-sets", map[string]string{
"scopeId": mockVariables[0].CreationScopeId,
"scope": "organization",
}, gomock.Any()).
Do(func(path string, request interface{}, response *[]ConfigurationSet) {
*response = mockVariables
}).Times(1)

variables, _ = apiClient.ConfigurationSets("ORGANIZATION", mockVariables[0].CreationScopeId)
})

It("Should return configuration sets", func() {
Expect(variables).To(Equal(mockVariables))
})
})
})
83 changes: 83 additions & 0 deletions env0/data_variable_set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package env0

import (
"context"

"github.com/env0/terraform-provider-env0/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataVariableSet() *schema.Resource {
return &schema.Resource{
ReadContext: dataVariableSetRead,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Description: "the name of the variable set",
Required: true,
},
"scope": {
Type: schema.TypeString,
Description: "the scope of the variable set. Valid values: 'ORGANIZATION', or 'PROJECT'",
Required: true,
ValidateDiagFunc: NewStringInValidator([]string{"ORGANIZATION", "PROJECT"}),
},
"project_id": {
Type: schema.TypeString,
Description: "the id of the 'PROJECT' scope. Is not required for 'ORGANIZATION' scope",
Optional: true,
},
"id": {
Type: schema.TypeString,
Description: "the id variable set",
Computed: true,
},
},
}
}

func dataVariableSetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
resource := struct {
Name string
Scope string
ProjectId string
}{}

if err := readResourceData(&resource, d); err != nil {
return diag.Errorf("schema resource data deserialization failed: %v", err)
}

apiClient := meta.(client.ApiClientInterface)

var scopeId string

switch resource.Scope {
case "ORGANIZATION":
var err error
scopeId, err = apiClient.OrganizationId()
if err != nil {
return diag.Errorf("could not get organization id: %v", err)
}
case "PROJECT":
if resource.ProjectId == "" {
return diag.Errorf("'project_id' is required")
}
scopeId = resource.ProjectId
}

variableSets, err := apiClient.ConfigurationSets(resource.Scope, scopeId)
if err != nil {
return diag.Errorf("could not get variable sets: %v", err)
}

for _, variableSet := range variableSets {
if variableSet.Name == resource.Name {
d.SetId(variableSet.Id)
return nil
}
}

return diag.Errorf("variable set not found")
}
158 changes: 158 additions & 0 deletions env0/data_variable_set_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package env0

import (
"errors"
"regexp"
"testing"

"github.com/env0/terraform-provider-env0/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestVariableSetDataSource(t *testing.T) {
projectId := "project_id"
organizationId := "organization_id"

v1 := client.ConfigurationSet{
Id: "id1",
Name: "name1",
CreationScopeId: projectId,
}

v2 := client.ConfigurationSet{
Id: "id2",
Name: "name2",
CreationScopeId: projectId,
}

v3 := client.ConfigurationSet{
Id: "id3",
Name: "name3",
CreationScopeId: organizationId,
}

v4 := client.ConfigurationSet{
Id: "id4",
Name: "name4",
CreationScopeId: "some_other_id",
}

resourceType := "env0_variable_set"
resourceName := "test_variable_set"
accessor := dataSourceAccessor(resourceType, resourceName)

getConfig := func(name string, scope string, projectId string) string {
fields := map[string]interface{}{"name": name, "scope": scope}
if projectId != "" {
fields["project_id"] = projectId
}
return dataSourceConfigCreate(resourceType, resourceName, fields)
}

mockVariableSetsCall := func(scope string, scopeId string, returnValue []client.ConfigurationSet) func(mockFunc *client.MockApiClientInterface) {
return func(mock *client.MockApiClientInterface) {
if organizationId != "" {
mock.EXPECT().OrganizationId().AnyTimes().Return(organizationId, nil)
}
mock.EXPECT().ConfigurationSets(scope, scopeId).AnyTimes().Return(returnValue, nil)
}
}

t.Run("project id scope", func(t *testing.T) {
runUnitTest(t,
resource.TestCase{
Steps: []resource.TestStep{
{
Config: getConfig(v2.Name, "PROJECT", v2.CreationScopeId),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", v2.Id),
),
},
},
},
mockVariableSetsCall("PROJECT", projectId, []client.ConfigurationSet{
v4, v1, v2,
}),
)
})

t.Run("organization id scope", func(t *testing.T) {
runUnitTest(t,
resource.TestCase{
Steps: []resource.TestStep{
{
Config: getConfig(v3.Name, "ORGANIZATION", ""),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", v3.Id),
),
},
},
},
mockVariableSetsCall("ORGANIZATION", organizationId, []client.ConfigurationSet{
v4, v3,
}),
)
})

t.Run("name not found", func(t *testing.T) {
runUnitTest(t,
resource.TestCase{
Steps: []resource.TestStep{
{
Config: getConfig("name that isn't found", "PROJECT", v2.CreationScopeId),
ExpectError: regexp.MustCompile("variable set not found"),
},
},
},
mockVariableSetsCall("PROJECT", projectId, []client.ConfigurationSet{
v4, v1, v2, v3,
}),
)
})

t.Run("get configuration sets api call failed", func(t *testing.T) {
runUnitTest(t,
resource.TestCase{
Steps: []resource.TestStep{
{
Config: getConfig(v2.Name, "PROJECT", v2.CreationScopeId),
ExpectError: regexp.MustCompile("could not get variable sets: error"),
},
},
},
func(mock *client.MockApiClientInterface) {
mock.EXPECT().ConfigurationSets("PROJECT", projectId).AnyTimes().Return(nil, errors.New("error"))
},
)
})

t.Run("get organization id api call failed", func(t *testing.T) {
runUnitTest(t,
resource.TestCase{
Steps: []resource.TestStep{
{
Config: getConfig(v3.Name, "ORGANIZATION", ""),
ExpectError: regexp.MustCompile("could not get organization id: error"),
},
},
},
func(mock *client.MockApiClientInterface) {
mock.EXPECT().OrganizationId().AnyTimes().Return("", errors.New("error"))
},
)
})

t.Run("project_id is required", func(t *testing.T) {
runUnitTest(t,
resource.TestCase{
Steps: []resource.TestStep{
{
Config: getConfig(v2.Name, "PROJECT", ""),
ExpectError: regexp.MustCompile("'project_id' is required"),
},
},
},
func(mock *client.MockApiClientInterface) {},
)
})
}
1 change: 1 addition & 0 deletions env0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func Provider(version string) plugin.ProviderFunc {
"env0_custom_flow": dataCustomFlow(),
"env0_projects": dataProjects(),
"env0_module_testing_project": dataModuleTestingProject(),
"env0_variable_set": dataVariableSet(),
},
ResourcesMap: map[string]*schema.Resource{
"env0_project": resourceProject(),
Expand Down
1 change: 0 additions & 1 deletion env0/resource_team_organization_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ func resourceTeamOrganizationAssignmentCreateOrUpdate(ctx context.Context, d *sc
organizationId, err := apiClient.OrganizationId()
if err != nil {
return diag.Errorf("could not get organization id: %v", err)

}

var payload client.TeamRoleAssignmentCreateOrUpdatePayload
Expand Down
Loading

0 comments on commit bc149f5

Please sign in to comment.