Skip to content

Commit

Permalink
feature - Add data source for all Tableau users
Browse files Browse the repository at this point in the history
  • Loading branch information
Gary James authored and Gary James committed Nov 25, 2024
1 parent 187fe3a commit 7811577
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 0 deletions.
5 changes: 5 additions & 0 deletions docs/data-sources/datasources.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ description: |-

Retrieve datasource details as a list of datasources available to read

## Example Usage

```terraform
data "tableau_groups" "example" {
}
```

<!-- schema generated by tfplugindocs -->
## Schema
Expand Down
5 changes: 5 additions & 0 deletions docs/data-sources/groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ description: |-

Retrieve groups details

## Example Usage

```terraform
data "tableau_datasources" "example" {
}
```

<!-- schema generated by tfplugindocs -->
## Schema
Expand Down
36 changes: 36 additions & 0 deletions docs/data-sources/users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "tableau_users Data Source - terraform-provider-tableau"
subcategory: ""
description: |-
Retrieve user details as a list of users available to read
---

# tableau_users (Data Source)

Retrieve user details as a list of users available to read

## Example Usage

```terraform
data "tableau_users" "example" {
}
```

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

### Read-Only

- `id` (String) ID of the users
- `users` (Attributes List) List of users and their attributes (see [below for nested schema](#nestedatt--users))

<a id="nestedatt--users"></a>
### Nested Schema for `users`

Read-Only:

- `email` (String) User email
- `id` (String) ID of the user
- `name` (String) Name for the user
- `site_role` (String) Site role for the user
2 changes: 2 additions & 0 deletions examples/data-sources/tableau_datasources/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
data "tableau_groups" "example" {
}
2 changes: 2 additions & 0 deletions examples/data-sources/tableau_groups/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
data "tableau_datasources" "example" {
}
2 changes: 2 additions & 0 deletions examples/data-sources/tableau_users/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
data "tableau_users" "example" {
}
1 change: 1 addition & 0 deletions tableau/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ func (p *tableauProvider) DataSources(_ context.Context) []func() datasource.Dat
GroupDataSource,
GroupsDataSource,
UserDataSource,
UsersDataSource,
ProjectDataSource,
ProjectsDataSource,
SiteDataSource,
Expand Down
61 changes: 61 additions & 0 deletions tableau/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
)

Expand All @@ -24,6 +25,66 @@ type UserResponse struct {
User User `json:"user"`
}

type UsersResponse struct {
Users []User `json:"user"`
}

type UserListResponse struct {
UsersResponse UsersResponse `json:"users"`
Pagination PaginationDetails `json:"pagination"`
}

func (c *Client) GetUsers() ([]User, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/users", c.ApiUrl), nil)
if err != nil {
return nil, err
}

body, err := c.doRequest(req)
if err != nil {
return nil, err
}

userListResponse := UserListResponse{}
err = json.Unmarshal(body, &userListResponse)
if err != nil {
return nil, err
}

// TODO: Generalise pagination handling and use elsewhere
pageNumber, totalPageCount, totalAvailable, err := GetPaginationNumbers(userListResponse.Pagination)
if err != nil {
return nil, err
}

allUsers := make([]User, 0, totalAvailable)
for _, user := range userListResponse.UsersResponse.Users {
allUsers = append(allUsers, user)
}

for page := pageNumber + 1; page <= totalPageCount; page++ {
fmt.Printf("Searching page %d", page)
req, err = http.NewRequest("GET", fmt.Sprintf("%s/users?pageNumber=%s", c.ApiUrl, strconv.Itoa(page)), nil)
if err != nil {
return nil, err
}
body, err = c.doRequest(req)
if err != nil {
return nil, err
}
userListResponse = UserListResponse{}
err = json.Unmarshal(body, &userListResponse)
if err != nil {
return nil, err
}
for _, user := range userListResponse.UsersResponse.Users {
allUsers = append(allUsers, user)
}
}

return allUsers, nil
}

func (c *Client) GetUser(userID string) (*User, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/users/%s/", c.ApiUrl, userID), nil)
if err != nil {
Expand Down
115 changes: 115 additions & 0 deletions tableau/users_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package tableau

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

var (
_ datasource.DataSource = &usersDataSource{}
_ datasource.DataSourceWithConfigure = &usersDataSource{}
)

func UsersDataSource() datasource.DataSource {
return &usersDataSource{}
}

type usersDataSource struct {
client *Client
}

type usersNestedDataModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Email types.String `tfsdk:"email"`
SiteRole types.String `tfsdk:"site_role"`
}

type usersDataSourceModel struct {
ID types.String `tfsdk:"id"`
Users []usersNestedDataModel `tfsdk:"users"`
}

func (d *usersDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_users"
}

func (d *usersDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Retrieve user details as a list of users available to read",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "ID of the users",
},
"users": schema.ListNestedAttribute{
Description: "List of users and their attributes",
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "ID of the user",
},
"name": schema.StringAttribute{
Computed: true,
Description: "Name for the user",
},
"email": schema.StringAttribute{
Computed: true,
Description: "User email",
},
"site_role": schema.StringAttribute{
Computed: true,
Description: "Site role for the user",
},
},
},
},
},
}
}

func (d *usersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state usersDataSourceModel

resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)

users, err := d.client.GetUsers()
if err != nil {
resp.Diagnostics.AddError(
"Unable to Read Tableau Users",
err.Error(),
)
return
}

for _, user := range users {
userDataSourceModel := usersNestedDataModel{
ID: types.StringValue(user.ID),
Name: types.StringValue(user.Name),
Email: types.StringValue(user.Email),
SiteRole: types.StringValue(user.SiteRole),
}
state.Users = append(state.Users, userDataSourceModel)
}

state.ID = types.StringValue("allUsers")

diags := resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

func (d *usersDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

d.client = req.ProviderData.(*Client)
}
24 changes: 24 additions & 0 deletions tableau/users_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package tableau

import (
"testing"

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

func TestAccUsersDataSource(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: providerConfig + `
data "tableau_users" "test" {
}`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.tableau_users.test", "id"),
resource.TestCheckResourceAttrSet("data.tableau_users.test", "users.#"),
),
},
},
})
}

0 comments on commit 7811577

Please sign in to comment.