From 7c7e0208fd1869d088d756b5840329eae495067e Mon Sep 17 00:00:00 2001 From: Nabil-Lahssini <61270565+Nabil-Lahssini@users.noreply.github.com> Date: Mon, 27 Mar 2023 16:18:35 +0200 Subject: [PATCH] Add user attribute member resource (#44) * Add user attribute member resource * Documentation generation * Remove import method; N/A --- docs/resources/user_attribute_member.md | 91 +++++++++++++ .../looker_user_attribute_member/resource.tf | 17 +++ .../resource.tfshow | 32 +++++ internal/provider/provider.go | 1 + internal/provider/resource_user_attribute.go | 1 + .../resource_user_attribute_member.go | 128 ++++++++++++++++++ pkg/lookergo/client.go | 32 ++++- pkg/lookergo/user_attribute.go | 23 ++++ 8 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 docs/resources/user_attribute_member.md create mode 100644 examples/resources/looker_user_attribute_member/resource.tf create mode 100644 examples/resources/looker_user_attribute_member/resource.tfshow create mode 100644 internal/provider/resource_user_attribute_member.go diff --git a/docs/resources/user_attribute_member.md b/docs/resources/user_attribute_member.md new file mode 100644 index 0000000..4248550 --- /dev/null +++ b/docs/resources/user_attribute_member.md @@ -0,0 +1,91 @@ +--- +page_title: "looker_user_attribute_member Resource - terraform-provider-looker" +subcategory: "" +description: |- + +--- +# looker_user_attribute_member (Resource) + +## Example Usage +```terraform +resource "looker_user_attribute" "att" { + name = "attribute_name" + label = "attribute label" + type = "string" +} + +resource "looker_group" "my-group" { + name = "mygroup" +} + +resource "looker_user_attribute_member" "name" { + user_attribute_id = looker_user_attribute.att.id + group { + id = looker_group.my-group.id + value = "attribute-value" + } +} +``` + +## Example Output +```terraform +% terraform show +# looker_group.my-group: +resource "looker_group" "my-group" { + delete_on_destroy = true + id = "39" + name = "mygroup" + parent_groups = [] + roles = [] +} + +# looker_user_attribute.att: +resource "looker_user_attribute" "att" { + id = "35" + label = "attribute label" + name = "attribute_name" + type = "string" + user_can_edit = true + user_can_view = true + value_is_hidden = false +} + +# looker_user_attribute_member.name: +resource "looker_user_attribute_member" "name" { + id = "-" + user_attribute_id = "35" + + group { + id = "39" + name = "mygroup" + value = "attribute-value" + } +} +``` + + +## Schema + +### Required + +- `user_attribute_id` (String) + +### Optional + +- `group` (Block Set) (see [below for nested schema](#nestedblock--group)) + +### Read-Only + +- `id` (String) The ID of this resource. + + +### Nested Schema for `group` + +Required: + +- `value` (String) + +Read-Only: + +- `id` (String) The ID of this resource. +- `name` (String) diff --git a/examples/resources/looker_user_attribute_member/resource.tf b/examples/resources/looker_user_attribute_member/resource.tf new file mode 100644 index 0000000..482a477 --- /dev/null +++ b/examples/resources/looker_user_attribute_member/resource.tf @@ -0,0 +1,17 @@ +resource "looker_user_attribute" "att" { + name = "attribute_name" + label = "attribute label" + type = "string" +} + +resource "looker_group" "my-group" { + name = "mygroup" +} + +resource "looker_user_attribute_member" "name" { + user_attribute_id = looker_user_attribute.att.id + group { + id = looker_group.my-group.id + value = "attribute-value" + } +} \ No newline at end of file diff --git a/examples/resources/looker_user_attribute_member/resource.tfshow b/examples/resources/looker_user_attribute_member/resource.tfshow new file mode 100644 index 0000000..e998a6a --- /dev/null +++ b/examples/resources/looker_user_attribute_member/resource.tfshow @@ -0,0 +1,32 @@ +% terraform show +# looker_group.my-group: +resource "looker_group" "my-group" { + delete_on_destroy = true + id = "39" + name = "mygroup" + parent_groups = [] + roles = [] +} + +# looker_user_attribute.att: +resource "looker_user_attribute" "att" { + id = "35" + label = "attribute label" + name = "attribute_name" + type = "string" + user_can_edit = true + user_can_view = true + value_is_hidden = false +} + +# looker_user_attribute_member.name: +resource "looker_user_attribute_member" "name" { + id = "-" + user_attribute_id = "35" + + group { + id = "39" + name = "mygroup" + value = "attribute-value" + } +} \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 5f51d6b..3635d94 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -82,6 +82,7 @@ func New(version string) func() *schema.Provider { "looker_color_collection": resourceColorCollection(), "looker_permission_set": resourcePermissionSet(), "looker_user_attribute": resourceUserAttribute(), + "looker_user_attribute_member": resourceUserAttributeMember(), }, } diff --git a/internal/provider/resource_user_attribute.go b/internal/provider/resource_user_attribute.go index 5cd35c4..af26826 100644 --- a/internal/provider/resource_user_attribute.go +++ b/internal/provider/resource_user_attribute.go @@ -168,5 +168,6 @@ func resourceUserAttributeDelete(ctx context.Context, d *schema.ResourceData, m if err != nil { return diag.FromErr(err) } + d.SetId("") return diags } diff --git a/internal/provider/resource_user_attribute_member.go b/internal/provider/resource_user_attribute_member.go new file mode 100644 index 0000000..4164e74 --- /dev/null +++ b/internal/provider/resource_user_attribute_member.go @@ -0,0 +1,128 @@ +package provider + +import ( + "context" + + "github.com/devoteamgcloud/terraform-provider-looker/pkg/lookergo" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceUserAttributeMember() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceUserAttributeMemberCreate, + ReadContext: resourceUserAttributeMemberRead, + UpdateContext: resourceUserAttributeMemberUpdate, + DeleteContext: resourceUserAttributeMemberDelete, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "user_attribute_id": { + Type: schema.TypeString, + Required: true, + }, + "group": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceUserAttributeMemberCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Config).Api // .(*lookergo.Client) + var diags diag.Diagnostics + ID := d.Get("user_attribute_id").(string) + groupSet, ok := d.GetOk("group") + userAttrs := []lookergo.UserAttributeGroupValue{} + if ok { + for _, raw := range groupSet.(*schema.Set).List() { + obj := raw.(map[string]interface{}) + val := obj["id"].(string) + att := lookergo.UserAttributeGroupValue{} + att.GroupId = val + att.UserAttributeId = d.Get("user_attribute_id").(string) + att.Value = obj["value"].(string) + userAttrs = append(userAttrs, att) + } + } + _, _, err := c.UserAttributes.SetUserAttributeValue(ctx, userAttrs, ID) + if err != nil { + return diag.FromErr(err) + } + d.SetId("-") + resourceUserAttributeMemberRead(ctx, d, m) + return diags +} + +func resourceUserAttributeMemberRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Config).Api // .(*lookergo.Client) + var diags diag.Diagnostics + ID := d.Get("user_attribute_id").(string) + attrs, _, err := c.UserAttributes.GetUserAttributeValue(ctx, ID) + if err != nil { + return diag.FromErr(err) + } + var attrItems []interface{} + for _, attr := range *attrs { + group, _, err := c.Groups.Get(ctx, idAsInt(attr.GroupId)) + if err != nil { + return diag.FromErr(err) + } + attrItems = append(attrItems, map[string]interface{}{"id": idAsString(group.Id), "name": group.Name, "value": attr.Value}) + } + d.Set("group", attrItems) + return diags +} + +func resourceUserAttributeMemberUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Config).Api // .(*lookergo.Client) + ID := d.Get("user_attribute_id").(string) + groupSet, ok := d.GetOk("group") + userAttrs := []lookergo.UserAttributeGroupValue{} + if ok { + for _, raw := range groupSet.(*schema.Set).List() { + obj := raw.(map[string]interface{}) + val := obj["id"].(string) + att := lookergo.UserAttributeGroupValue{} + att.GroupId = val + att.UserAttributeId = d.Get("user_attribute_id").(string) + att.Value = obj["value"].(string) + userAttrs = append(userAttrs, att) + } + } + _, _, err := c.UserAttributes.SetUserAttributeValue(ctx, userAttrs, ID) + if err != nil { + return diag.FromErr(err) + } + d.SetId("-") + return resourceUserAttributeMemberRead(ctx, d, m) +} + +func resourceUserAttributeMemberDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*Config).Api // .(*lookergo.Client) + var diags diag.Diagnostics + att := []lookergo.UserAttributeGroupValue{} + ID := d.Get("user_attribute_id").(string) + c.UserAttributes.SetUserAttributeValue(ctx, att, ID) + d.SetId("") + return diags +} diff --git a/pkg/lookergo/client.go b/pkg/lookergo/client.go index 1f8590c..92d18d0 100644 --- a/pkg/lookergo/client.go +++ b/pkg/lookergo/client.go @@ -553,7 +553,7 @@ func StreamToString(stream io.Reader) string { } type service interface { - Group | User | CredentialsEmail | Role | PermissionSet | Session | Project | GitBranch | Folder | UserAttribute + Group | User | CredentialsEmail | Role | PermissionSet | Session | Project | GitBranch | Folder | UserAttribute | UserAttributeGroupValue } // addOptions - @@ -811,6 +811,36 @@ func doAddMember[T service](ctx context.Context, client *Client, path string, sv return svc, resp, err } +func doAddValue[T service](ctx context.Context, client *Client, path string, svc *[]T, addNew interface{}) (*[]T, *Response, error) { + + req, err := client.NewRequest(ctx, http.MethodPost, path, addNew) + if err != nil { + return nil, nil, err + } + + resp, err := client.Do(ctx, req, svc) + if err != nil { + return nil, resp, err + } + + return svc, resp, err +} + +func doDeleteX(ctx context.Context, client *Client, path string) (*Response, error) { + + req, err := client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + + resp, err := client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, err +} + func boolPtr(b bool) *bool { return &b } diff --git a/pkg/lookergo/user_attribute.go b/pkg/lookergo/user_attribute.go index 1d29f17..87b5252 100644 --- a/pkg/lookergo/user_attribute.go +++ b/pkg/lookergo/user_attribute.go @@ -2,6 +2,7 @@ package lookergo import ( "context" + "fmt" ) type UserAttributesResourceOp struct { @@ -25,11 +26,23 @@ type UserAttribute struct { HiddenValueDomainWhitelist *string `json:"hidden_value_domain_whitelist,omitempty"` // Destinations to which a hidden attribute may be sent. Once set, cannot be edited. } +type UserAttributeGroupValue struct { + Can map[string]bool `json:"can,omitempty"` // Operations the current user is able to perform on this object + Id string `json:"id,omitempty"` // Unique Id of this group-attribute relation + GroupId string `json:"group_id,omitempty"` // Id of group + UserAttributeId string `json:"user_attribute_id,omitempty"` // Id of user attribute + ValueIsHidden bool `json:"value_is_hidden,omitempty"` // If true, the "value" field will be null, because the attribute settings block access to this value + Rank int64 `json:"rank,omitempty"` // Precedence for resolving value for user + Value string `json:"value,omitempty"` // Value of user attribute for group +} + type UserAttributesResource interface { Get(context.Context, int) (*UserAttribute, *Response, error) Create(context.Context, *UserAttribute) (*UserAttribute, *Response, error) Update(context.Context, string, *UserAttribute) (*UserAttribute, *Response, error) Delete(context.Context, string) (*Response, error) + SetUserAttributeValue(context.Context, []UserAttributeGroupValue, string) (*[]UserAttributeGroupValue, *Response, error) + GetUserAttributeValue(context.Context, string) (*[]UserAttributeGroupValue, *Response, error) } func (s *UserAttributesResourceOp) Get(ctx context.Context, UserAttributeId int) (*UserAttribute, *Response, error) { @@ -47,3 +60,13 @@ func (s *UserAttributesResourceOp) Update(ctx context.Context, UserAttributeId s func (s *UserAttributesResourceOp) Delete(ctx context.Context, UserAttributeId string) (*Response, error) { return doDelete(ctx, s.client, UserAttributesBasePath, UserAttributeId) } + +func (s *UserAttributesResourceOp) SetUserAttributeValue(ctx context.Context, userAtt []UserAttributeGroupValue, attributeId string) (*[]UserAttributeGroupValue, *Response, error) { + path := fmt.Sprintf("%s/%s/group_values", UserAttributesBasePath, attributeId) + return doAddValue(ctx, s.client, path, new([]UserAttributeGroupValue), userAtt) +} + +func (s *UserAttributesResourceOp) GetUserAttributeValue(ctx context.Context, UserAttributeId string) (*[]UserAttributeGroupValue, *Response, error) { + path := fmt.Sprintf("%s/%s/group_values", UserAttributesBasePath, UserAttributeId) + return doGet(ctx, s.client, path, new([]UserAttributeGroupValue)) +}