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

Add doppler_project_role resource #88

Merged
merged 1 commit into from
Jun 3, 2024
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
41 changes: 41 additions & 0 deletions docs/resources/project_role.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
page_title: "doppler_project_role Resource - terraform-provider-doppler"
subcategory: ""
description: |-
Manage a Doppler project role.
---

# doppler_project_role (Resource)

Manage a Doppler project_role.

## Example Usage

```terraform
resource "doppler_project_role" "log_viewer" {
name = "Log Viewer"
permissions = ["enclave_config_logs"]
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) The name of the Doppler project role
- `permissions` (Set of String) A list of [Doppler project permissions](https://docs.doppler.com/reference/project_roles-create)

### Read-Only

- `id` (String) The ID of this resource.
- `identifier` (String) The role's unique identifier
- `is_custom_role` (Boolean) Whether or not the role is custom (as opposed to Doppler built-in)

## Import

Import is supported using the following syntax:

```shell
terraform import doppler_project_role.default <project-role-identifier>
```
63 changes: 62 additions & 1 deletion doppler/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,69 @@ func (client APIClient) DeleteProject(ctx context.Context, name string) error {
return nil
}

// Project Members
// Project Roles

func (client APIClient) CreateProjectRole(ctx context.Context, name string, permissions []string) (*ProjectRole, error) {
payload := map[string]interface{}{
"name": name,
"permissions": permissions,
}
body, err := json.Marshal(payload)
if err != nil {
return nil, &APIError{Err: err, Message: "Unable to serialize project role"}
}
response, err := client.PerformRequestWithRetry(ctx, "POST", "/v3/projects/roles", []QueryParam{}, body)
if err != nil {
return nil, err
}
var result CreateProjectRoleResponse
if err = json.Unmarshal(response.Body, &result); err != nil {
return nil, &APIError{Err: err, Message: "Unable to parse project role"}
}
return &result.Role, nil
}

func (client APIClient) GetProjectRole(ctx context.Context, identifier string) (*ProjectRole, error) {
response, err := client.PerformRequestWithRetry(ctx, "GET", fmt.Sprintf("/v3/projects/roles/role/%s", url.PathEscape(identifier)), []QueryParam{}, nil)
if err != nil {
return nil, err
}
var result UpdateProjectRoleResponse
if err = json.Unmarshal(response.Body, &result); err != nil {
return nil, &APIError{Err: err, Message: "Unable to parse project role"}
}
return &result.Role, nil
}

func (client APIClient) UpdateProjectRole(ctx context.Context, identifier string, name string, permissions []string) (*ProjectRole, error) {
payload := map[string]interface{}{
"name": name,
"permissions": permissions,
}
body, err := json.Marshal(payload)
if err != nil {
return nil, &APIError{Err: err, Message: "Unable to serialize project role"}
}
response, err := client.PerformRequestWithRetry(ctx, "PATCH", fmt.Sprintf("/v3/projects/roles/role/%s", url.PathEscape(identifier)), []QueryParam{}, body)
if err != nil {
return nil, err
}
var result UpdateProjectRoleResponse
if err = json.Unmarshal(response.Body, &result); err != nil {
return nil, &APIError{Err: err, Message: "Unable to parse project role"}
}
return &result.Role, nil
}

func (client APIClient) DeleteProjectRole(ctx context.Context, identifier string) error {
_, err := client.PerformRequestWithRetry(ctx, "DELETE", fmt.Sprintf("/v3/projects/roles/role/%s", url.PathEscape(identifier)), []QueryParam{}, nil)
if err != nil {
return err
}
return nil
}

// Project Members
func (client APIClient) CreateProjectMember(ctx context.Context, project string, memberType string, memberSlug string, role string, environments []string) (*ProjectMember, error) {
payload := map[string]interface{}{
"project": project,
Expand Down
20 changes: 20 additions & 0 deletions doppler/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,26 @@ type SimpleProjectRole struct {
Identifier string `json:"identifier"`
}

type ProjectRole struct {
Identifier string `json:"identifier"`
Name string `json:"name"`
Permissions []string `json:"permissions"`
CreatedAt string `json:"created_at"`
IsCustomRole bool `json:"is_custom_role"`
}

type GetProjectRoleResponse struct {
Role ProjectRole `json:"role"`
}

type CreateProjectRoleResponse struct {
Role ProjectRole `json:"role"`
}

type UpdateProjectRoleResponse struct {
Role ProjectRole `json:"role"`
}

type Group struct {
Slug string `json:"slug"`
Name string `json:"name"`
Expand Down
2 changes: 2 additions & 0 deletions doppler/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func Provider() *schema.Provider {
"doppler_config": resourceConfig(),
"doppler_service_token": resourceServiceToken(),

"doppler_project_role": resourceProjectRole(),

"doppler_service_account": resourceServiceAccount(),
"doppler_service_account_token": resourceServiceAccountToken(),

Expand Down
134 changes: 134 additions & 0 deletions doppler/resource_project_role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package doppler

import (
"context"

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

func resourceProjectRole() *schema.Resource {
return &schema.Resource{
CreateContext: resourceProjectRoleCreate,
ReadContext: resourceProjectRoleRead,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
UpdateContext: resourceProjectRoleUpdate,
DeleteContext: resourceProjectRoleDelete,
Schema: map[string]*schema.Schema{
"name": {
Description: "The name of the Doppler project role",
Type: schema.TypeString,
Required: true,
},
"permissions": {
Description: "A list of [Doppler project permissions](https://docs.doppler.com/reference/project_roles-create)",
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Required: true,
},
"identifier": {
Description: "The role's unique identifier",
Type: schema.TypeString,
Computed: true,
},
"is_custom_role": {
Description: "Whether or not the role is custom (as opposed to Doppler built-in)",
Type: schema.TypeBool,
Computed: true,
},
},
}
}

func updateProjectRoleData(d *schema.ResourceData, role *ProjectRole) diag.Diagnostics {
d.SetId(role.Identifier)

if err := d.Set("name", role.Name); err != nil {
return diag.FromErr(err)
}

if err := d.Set("permissions", role.Permissions); err != nil {
return diag.FromErr(err)
}

if err := d.Set("identifier", role.Identifier); err != nil {
return diag.FromErr(err)
}

if err := d.Set("is_custom_role", role.IsCustomRole); err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceProjectRoleCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(APIClient)

var diags diag.Diagnostics
name := d.Get("name").(string)
permissions := []string{}
for _, v := range d.Get("permissions").(*schema.Set).List() {
permissions = append(permissions, v.(string))
}

role, err := client.CreateProjectRole(ctx, name, permissions)
if err != nil {
return diag.FromErr(err)
}

diags = append(diags, updateProjectRoleData(d, role)...)
return diags
}

func resourceProjectRoleUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(APIClient)

var diags diag.Diagnostics
identifier := d.Id()
newName := d.Get("name").(string)
newPermissions := []string{}
for _, v := range d.Get("permissions").(*schema.Set).List() {
newPermissions = append(newPermissions, v.(string))
}

role, err := client.UpdateProjectRole(ctx, identifier, newName, newPermissions)
if err != nil {
return diag.FromErr(err)
}
diags = append(diags, updateProjectRoleData(d, role)...)
return diags
}

func resourceProjectRoleRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(APIClient)

var diags diag.Diagnostics

identifier := d.Id()

role, err := client.GetProjectRole(ctx, identifier)
if err != nil {
return handleNotFoundError(err, d)
}

diags = append(diags, updateProjectRoleData(d, role)...)
return diags
}

func resourceProjectRoleDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(APIClient)

var diags diag.Diagnostics

identifier := d.Id()
if err := client.DeleteProjectRole(ctx, identifier); err != nil {
return diag.FromErr(err)
}

return diags
}
4 changes: 4 additions & 0 deletions examples/resources/project_role.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "doppler_project_role" "log_viewer" {
name = "Log Viewer"
permissions = ["enclave_config_logs"]
}
24 changes: 24 additions & 0 deletions templates/resources/project_role.md.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
page_title: "doppler_project_role Resource - terraform-provider-doppler"
subcategory: ""
description: |-
Manage a Doppler project role.
---

# doppler_project_role (Resource)

Manage a Doppler project_role.

## Example Usage

{{tffile "examples/resources/project_role.tf"}}

{{ .SchemaMarkdown | trimspace }}

## Import

Import is supported using the following syntax:

```shell
terraform import doppler_project_role.default <project-role-identifier>
```