Skip to content

Commit

Permalink
feat(aiven_organization_user_list): add datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
byashimov committed Oct 18, 2024
1 parent c856ada commit 57dd049
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 10 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ nav_order: 1
<!-- Always keep the following header in place: -->
<!-- ## [MAJOR.MINOR.PATCH] - YYYY-MM-DD -->

## [MAJOR.MINOR.PATCH] - YYYY-MM-DD

- Add `aiven_organization_user_list` resource
- Run client-side validation for `aiven_kafka_schema` AVRO type schema

## [4.27.0] - 2024-10-09

- Remove `aiven_thanos` from beta resources
- Removes `receiver_ingesting_remote_write_uri` and `store_uri` Thanos connection info fields
- Adds `stringtype` to `flink_external_postgresql_user_config` service integration
- Fix `terraform import` for services with additional disk space or read replica service integration
- Run client-side validation for `aiven_kafka_schema` AVRO type schema

## [4.26.0] - 2024-09-25

Expand Down
53 changes: 53 additions & 0 deletions docs/data-sources/organization_user_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "aiven_organization_user_list Data Source - terraform-provider-aiven"
subcategory: ""
description: |-
List of users of the organization
---

# aiven_organization_user_list (Data Source)

List of users of the organization



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

### Optional

- `id` (String) Organization id. Example: `org12345678`.
- `name` (String) Organization name. Example: `aiven`.

### Read-Only

- `users` (List of Object) List of users of the organization (see [below for nested schema](#nestedatt--users))

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

Read-Only:

- `is_super_admin` (Boolean)
- `join_time` (String)
- `last_activity_time` (String)
- `user_id` (String)
- `user_info` (List of Object) (see [below for nested schema](#nestedobjatt--users--user_info))

<a id="nestedobjatt--users--user_info"></a>
### Nested Schema for `users.user_info`

Read-Only:

- `city` (String)
- `country` (String)
- `create_time` (String)
- `department` (String)
- `is_application_user` (Boolean)
- `job_title` (String)
- `managed_by_scim` (Boolean)
- `managing_organization_id` (String)
- `real_name` (String)
- `state` (String)
- `user_email` (String)
6 changes: 3 additions & 3 deletions internal/acctest/acctest.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ func GetTestAivenClient() *aiven.Client {
return testAivenClient
}

func GetTestGenAivenClient() avngen.Client {
func GetTestGenAivenClient() (avngen.Client, error) {
client, err := common.NewAivenGenClient()
if err != nil {
log.Panicf("test generated client error: %s", err)
return nil, fmt.Errorf("test generated client error: %w", err)
}
return client
return client, nil
}

// commonTestDependencies is a struct that contains common dependencies that are used by acceptance tests.
Expand Down
41 changes: 36 additions & 5 deletions internal/schemautil/schemautil.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,24 +386,24 @@ func ResourceDataGet(d *schema.ResourceData, dto any, fns ...KVModifier) error {
k, value = f(k, value)
}

m[k] = serializeValue(value)
m[k] = serializeGet(value)
}

return Remarshal(&m, dto)
}

func serializeValue(value any) any {
func serializeGet(value any) any {
switch t := value.(type) {
case *schema.Set:
return serializeValue(t.List())
return serializeGet(t.List())
case []any:
for i, v := range t {
t[i] = serializeValue(v)
t[i] = serializeGet(v)
}
return t
case map[string]any:
for k, v := range t {
t[k] = serializeValue(v)
t[k] = serializeGet(v)
}
}
return value
Expand Down Expand Up @@ -442,6 +442,7 @@ func ResourceDataSet(s map[string]*schema.Schema, d *schema.ResourceData, dto an
}
}

m = serializeSet(s, m)
for k := range s {
if v, ok := m[k]; ok {
if err = d.Set(k, v); err != nil {
Expand All @@ -452,6 +453,36 @@ func ResourceDataSet(s map[string]*schema.Schema, d *schema.ResourceData, dto an
return nil
}

func serializeSet(s map[string]*schema.Schema, m map[string]any) map[string]any {
for k, prop := range s {
value, ok := m[k]
if !ok {
continue
}

res, ok := prop.Elem.(*schema.Resource)
if !ok {
continue
}

// When we have an object, we need to convert it to a list.
// So there is no difference between a single object and a list of objects.
var items []any
switch element := value.(type) {
case map[string]any:
items = append(items, serializeSet(res.Schema, element))
case []any:
for _, v := range element {
items = append(items, serializeSet(res.Schema, v.(map[string]any)))
}
}

m[k] = items
}

return m
}

// RenameAliases renames field names on object top level
func RenameAliases(aliases map[string]string) KVModifier {
return func(k string, v any) (string, any) {
Expand Down
1 change: 1 addition & 0 deletions internal/sdkprovider/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func Provider(version string) (*schema.Provider, error) {
// organization
"aiven_organizational_unit": organization.DatasourceOrganizationalUnit(),
"aiven_organization_user": organization.DatasourceOrganizationUser(),
"aiven_organization_user_list": organization.DatasourceOrganizationUserList(),
"aiven_organization_user_group": organization.DatasourceOrganizationUserGroup(),
"aiven_organization_application_user": organization.DatasourceOrganizationApplicationUser(),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/stretchr/testify/require"

acc "github.com/aiven/terraform-provider-aiven/internal/acctest"
)
Expand Down Expand Up @@ -41,7 +42,8 @@ func TestAccAivenOrganizationPermission_basic(t *testing.T) {
Config: testAccOrganizationPermissionResource(rName, ""),
Check: func(s *terraform.State) error {
ctx := context.Background()
client := acc.GetTestGenAivenClient()
client, err := acc.GetTestGenAivenClient()
require.NoError(t, err)

for _, r := range s.RootModule().Resources {
if r.Type != "aiven_project" {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package organization

import (
"context"
"fmt"
"strings"

avngen "github.com/aiven/go-client-codegen"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/aiven/terraform-provider-aiven/internal/common"
"github.com/aiven/terraform-provider-aiven/internal/schemautil"
)

func datasourceOrganizationUserListSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Optional: true,
Description: "Organization id. Example: `org12345678`.",
ConflictsWith: []string{"name"},
},
"name": {
Type: schema.TypeString,
Optional: true,
Description: "Organization name. Example: `aiven`.",
ConflictsWith: []string{"id"},
},
"users": {
Type: schema.TypeList,
Computed: true,
Description: "List of users of the organization",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"is_super_admin": {
Type: schema.TypeBool,
Computed: true,
Description: "Super admin state of the organization user",
},
"join_time": {
Type: schema.TypeString,
Computed: true,
Description: "Join time",
},
"last_activity_time": {
Type: schema.TypeString,
Computed: true,
Description: "Last activity time",
},
"user_id": {
Type: schema.TypeString,
Computed: true,
Description: "User ID",
},
"user_info": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"city": {
Type: schema.TypeString,
Computed: true,
Description: "City",
},
"country": {
Type: schema.TypeString,
Computed: true,
Description: "Country",
},
"create_time": {
Type: schema.TypeString,
Computed: true,
Description: "Creation time",
},
"department": {
Type: schema.TypeString,
Computed: true,
Description: "Department",
},
"is_application_user": {
Type: schema.TypeBool,
Computed: true,
Description: "Is Application User",
},
"job_title": {
Type: schema.TypeString,
Computed: true,
Description: "Job Title",
},
"managed_by_scim": {
Type: schema.TypeBool,
Computed: true,
Description: "Managed By Scim",
},
"managing_organization_id": {
Type: schema.TypeString,
Computed: true,
Description: "Managing Organization ID",
},
"real_name": {
Type: schema.TypeString,
Computed: true,
Description: "Real Name",
},
"state": {
Type: schema.TypeString,
Computed: true,
Description: "State",
},
"user_email": {
Type: schema.TypeString,
Computed: true,
Description: "User Email",
},
},
},
},
},
},
},
}
}

func DatasourceOrganizationUserList() *schema.Resource {
return &schema.Resource{
ReadContext: common.WithGenClient(datasourceOrganizationUserListRead),
Description: "List of users of the organization",
Schema: datasourceOrganizationUserListSchema(),
}
}

func datasourceOrganizationUserListRead(ctx context.Context, d *schema.ResourceData, client avngen.Client) error {
organizationID := d.Get("id").(string)
if organizationID == "" {
name := d.Get("name").(string)
if name == "" {
return fmt.Errorf("either id or name must be specified")
}

id, err := GetOrganizationByName(ctx, client, name)
if err != nil {
return err
}
organizationID = id
}

list, err := client.OrganizationUserList(ctx, organizationID)
if err != nil {
return fmt.Errorf("cannot get organization %q user list: %w", organizationID, err)
}

d.SetId(organizationID)
users := map[string]any{"users": list}
return schemautil.ResourceDataSet(datasourceOrganizationUserListSchema(), d, users)
}

func GetOrganizationByName(ctx context.Context, client avngen.Client, name string) (string, error) {
ids := make([]string, 0)
list, err := client.UserOrganizationsList(ctx)
if err != nil {
return "", err
}

for _, o := range list {
// Organization name is not unique
if o.OrganizationName == name {
ids = append(ids, o.OrganizationId)
}
}

switch len(ids) {
case 0:
return "", fmt.Errorf("organization %q not found", name)
case 1:
return ids[0], nil
}
return "", fmt.Errorf("multiple organizations %q found, ids: %s", name, strings.Join(ids, ", "))
}
Loading

0 comments on commit 57dd049

Please sign in to comment.