Skip to content

Commit

Permalink
refactor: turn lists into set, cause order does not matter (#103)
Browse files Browse the repository at this point in the history
* chore: turn lists into set, cause order does not matter

* Remove debug to reduce noise

* Remove ordering requirement on tests
  • Loading branch information
gastonfournier authored Dec 21, 2023
1 parent 83ee02b commit 00fd373
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,6 @@ jobs:
- run: go test -v -cover ./internal/provider/
env:
TF_ACC: "1"
TF_LOG: "debug"
#TF_LOG: "debug"
UNLEASH_ENTERPRISE: "${{ contains(matrix.docker-image, 'unleash-enterprise') }}"
timeout-minutes: 3
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ services:
# communicates with server-side SDKs and the Unleash Proxy
unleash:
image: ${UNLEASH_DOCKER_IMAGE:-unleashorg/unleash-server:latest}
pull_policy: "always"
ports:
- "4242:4242" # TODO expose at random port (useful for testing)
environment:
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/api_token.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ resource "unleash_api_token" "admin_no_expire" {
- `environment` (String) An environment the token has access to.
- `expires_at` (String) When the token expires
- `project` (String) A project the token belongs to.
- `projects` (List of String) The list of projects this token has access to. If the token has access to specific projects they will be listed here. If the token has access to all projects it will be represented as `[*]`.
- `projects` (Set of String) The list of projects this token has access to. If the token has access to specific projects they will be listed here. If the token has access to all projects it will be represented as `[*]`.
- `token_name` (String) The name of the token.
- `type` (String) The type of the token.

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/project_access.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ resource "unleash_project_access" "sample_project_access" {
### Required

- `project` (String) Project identifier.
- `roles` (Attributes List) Roles available in this project with their members. (see [below for nested schema](#nestedatt--roles))
- `roles` (Attributes Set) Roles available in this project with their members. (see [below for nested schema](#nestedatt--roles))

<a id="nestedatt--roles"></a>
### Nested Schema for `roles`
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/role.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ resource "unleash_role" "project_role" {

- `description` (String) A more detailed description of the role and what use it's intended for.
- `name` (String) The name of this role.
- `permissions` (Attributes List) A more detailed description of the role and what use it's intended for. (see [below for nested schema](#nestedatt--permissions))
- `permissions` (Attributes Set) A more detailed description of the role and what use it's intended for. (see [below for nested schema](#nestedatt--permissions))
- `type` (String) A role can either be a global root role (applies to all roles) or a role role.

### Read-Only
Expand Down
8 changes: 4 additions & 4 deletions internal/provider/api_token_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type apiTokenResourceModel struct {
// The project this token belongs to.
Project types.String `tfsdk:"project"`
// The list of projects this token has access to. If the token has access to specific projects they will be listed here. If the token has access to all projects it will be represented as `[*]`
Projects types.List `tfsdk:"projects"`
Projects types.Set `tfsdk:"projects"`
// The token's expiration date. NULL if the token doesn't have an expiration set.
ExpiresAt types.String `tfsdk:"expires_at"`
}
Expand Down Expand Up @@ -93,7 +93,7 @@ func (r *apiTokenResource) Schema(_ context.Context, _ resource.SchemaRequest, r
Optional: true,
Computed: true,
},
"projects": schema.ListAttribute{
"projects": schema.SetAttribute{
Description: "The list of projects this token has access to. If the token has access to specific projects they will be listed here. If the token has access to all projects it will be represented as `[*]`.",
Optional: true,
Computed: true,
Expand Down Expand Up @@ -170,7 +170,7 @@ func (r *apiTokenResource) Create(ctx context.Context, req resource.CreateReques
tflog.Debug(ctx, fmt.Sprintf("Token has projects: %+v but plan is %+v", token.Projects, plan.Projects))
} else {
if token.Projects != nil {
newState.Projects, _ = basetypes.NewListValueFrom(ctx, types.StringType, token.Projects)
newState.Projects, _ = basetypes.NewSetValueFrom(ctx, types.StringType, token.Projects)
}
tflog.Debug(ctx, fmt.Sprintf("Projects not null: %+v", token.Projects))
}
Expand Down Expand Up @@ -217,7 +217,7 @@ func (r *apiTokenResource) Read(ctx context.Context, req resource.ReadRequest, r
state.Type = types.StringValue(token.Type)
state.Project = types.StringValue(token.Project)
if token.Projects != nil {
state.Projects, _ = basetypes.NewListValueFrom(ctx, types.StringType, token.Projects)
state.Projects, _ = basetypes.NewSetValueFrom(ctx, types.StringType, token.Projects)
}
if token.ExpiresAt.IsSet() && token.ExpiresAt.Get() != nil {
state.ExpiresAt = types.StringValue(token.ExpiresAt.Get().Format(time.RFC3339))
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/project_access_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (r *projectAccessResource) Schema(_ context.Context, _ resource.SchemaReque
Description: "Project identifier.",
Required: true,
},
"roles": schema.ListNestedAttribute{
"roles": schema.SetNestedAttribute{
Description: "Roles available in this project with their members.",
Required: true,
NestedObject: schema.NestedAttributeObject{
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/role_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (r *roleResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
Description: "A more detailed description of the role and what use it's intended for.",
Required: true,
},
"permissions": schema.ListNestedAttribute{
"permissions": schema.SetNestedAttribute{
Description: "A more detailed description of the role and what use it's intended for.",
Required: true,
NestedObject: schema.NestedAttributeObject{
Expand Down
48 changes: 40 additions & 8 deletions internal/provider/role_resource_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
package provider

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

// customCheckRolePermissionExists checks if a specific permission (and optionally an environment) exists in a role's permissions list.
func customCheckRolePermissionExists(resourceName string, permissionName string, environment ...string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("Not found: %s", resourceName)
}

for i := 0; ; i++ {
permissionAttrKey := fmt.Sprintf("permissions.%d.name", i)
permission, ok := rs.Primary.Attributes[permissionAttrKey]
if !ok {
break // Exit the loop if no more permissions are found
}
if permission == permissionName {
// Check for environment if provided
if len(environment) > 0 {
environmentAttrKey := fmt.Sprintf("permissions.%d.environment", i)
env, ok := rs.Primary.Attributes[environmentAttrKey]
if !ok || env != environment[0] {
continue // Skip to the next permission if environment does not match
}
}
return nil
}
}

return fmt.Errorf("Permission %s with environment %v not found in resource %s", permissionName, environment, resourceName)
}
}

func TestAccRoleResource(t *testing.T) {
if os.Getenv("UNLEASH_ENTERPRISE") != "true" {
t.Skip("Skipping enterprise tests")
Expand All @@ -31,8 +64,8 @@ func TestAccRoleResource(t *testing.T) {
resource.TestCheckResourceAttrSet("unleash_role.custom_root_role", "id"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "name", "A custom role"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "type", "root-custom"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "permissions.0.name", "CREATE_PROJECT"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "permissions.1.name", "UPDATE_PROJECT"),
customCheckRolePermissionExists("unleash_role.custom_root_role", "CREATE_PROJECT"),
customCheckRolePermissionExists("unleash_role.custom_root_role", "UPDATE_PROJECT"),
),
},
// Test update name and permissions
Expand All @@ -52,8 +85,8 @@ func TestAccRoleResource(t *testing.T) {
resource.TestCheckResourceAttrSet("unleash_role.custom_root_role", "id"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "name", "Renamed custom role"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "type", "root-custom"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "permissions.0.name", "CREATE_SEGMENT"),
resource.TestCheckResourceAttr("unleash_role.custom_root_role", "permissions.1.name", "UPDATE_SEGMENT"),
customCheckRolePermissionExists("unleash_role.custom_root_role", "CREATE_SEGMENT"),
customCheckRolePermissionExists("unleash_role.custom_root_role", "UPDATE_SEGMENT"),
),
},
{
Expand All @@ -76,10 +109,9 @@ func TestAccRoleResource(t *testing.T) {
resource.TestCheckResourceAttrSet("unleash_role.project_role", "id"),
resource.TestCheckResourceAttr("unleash_role.project_role", "name", "Custom project role"),
resource.TestCheckResourceAttr("unleash_role.project_role", "type", "custom"),
resource.TestCheckResourceAttr("unleash_role.project_role", "permissions.0.name", "CREATE_FEATURE"),
resource.TestCheckResourceAttr("unleash_role.project_role", "permissions.1.name", "DELETE_FEATURE"),
resource.TestCheckResourceAttr("unleash_role.project_role", "permissions.2.name", "UPDATE_FEATURE_ENVIRONMENT"),
resource.TestCheckResourceAttr("unleash_role.project_role", "permissions.2.environment", "development"),
customCheckRolePermissionExists("unleash_role.project_role", "CREATE_FEATURE"),
customCheckRolePermissionExists("unleash_role.project_role", "DELETE_FEATURE"),
customCheckRolePermissionExists("unleash_role.project_role", "UPDATE_FEATURE_ENVIRONMENT", "development"),
),
},
},
Expand Down

0 comments on commit 00fd373

Please sign in to comment.