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

feat: migrate users data source to tf framework #795

Merged
merged 8 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
54 changes: 31 additions & 23 deletions docs/data-sources/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,56 @@ data "octopusdeploy_users" "example" {

### Optional

- `filter` (String) A filter with which to search.
- `filter` (String) A filter search by username, display name or email
- `ids` (List of String) A filter to search by a list of IDs.
- `skip` (Number) A filter to specify the number of items to skip in the response.
- `space_id` (String) A Space ID to filter by. Will revert what is specified on the provider if not set.
- `space_id` (String) The space ID associated with this user.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think space_id should be removed entirely since users aren't space-scoped but I've left it in for backwards compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, good point. Lets mark it as deprecated.

- `take` (Number) A filter to specify the number of items to take (or return) in the response.

### Read-Only

- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified.
- `users` (List of Object) A list of users that match the filter(s). (see [below for nested schema](#nestedatt--users))
- `id` (String) The unique ID for this resource.
- `users` (Attributes List) (see [below for nested schema](#nestedatt--users))

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

Required:

- `display_name` (String) The display name of this resource.
- `username` (String) The username associated with this resource.

Optional:

- `can_password_be_edited` (Boolean) Specifies whether or not the password can be edited.
- `email_address` (String) The email address of this resource.
- `identity` (Attributes Set) The identities associated with the user. (see [below for nested schema](#nestedatt--users--identity))
- `is_active` (Boolean) Specifies whether or not the user is active.
- `is_requestor` (Boolean) Specifies whether or not the user is the requestor.
- `is_service` (Boolean) Specifies whether or not the user is a service account.

Read-Only:

- `can_password_be_edited` (Boolean)
- `display_name` (String)
- `email_address` (String)
- `id` (String)
- `identity` (Set of Object) (see [below for nested schema](#nestedobjatt--users--identity))
- `is_active` (Boolean)
- `is_requestor` (Boolean)
- `is_service` (Boolean)
- `password` (String)
- `username` (String)

<a id="nestedobjatt--users--identity"></a>
- `id` (String) The unique ID for this resource.

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

Read-Only:

- `claim` (Set of Object) (see [below for nested schema](#nestedobjatt--users--identity--claim))
- `provider` (String)
- `claim` (Attributes Set) The claim associated with the identity. (see [below for nested schema](#nestedatt--users--identity--claim))
- `provider` (String) The identity provider.

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

Read-Only:
Required:

- `name` (String) The name of this resource.
- `value` (String) The value of this resource.

Optional:

- `is_identifying_claim` (Boolean)
- `name` (String)
- `value` (String)
- `is_identifying_claim` (Boolean) Specifies whether or not the claim is an identifying claim.


46 changes: 0 additions & 46 deletions octopusdeploy/data_source_users.go

This file was deleted.

1 change: 0 additions & 1 deletion octopusdeploy/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ func Provider() *schema.Provider {
"octopusdeploy_polling_tentacle_deployment_targets": dataSourcePollingTentacleDeploymentTargets(),
"octopusdeploy_ssh_connection_deployment_targets": dataSourceSSHConnectionDeploymentTargets(),
"octopusdeploy_teams": dataSourceTeams(),
"octopusdeploy_users": dataSourceUsers(),
"octopusdeploy_user_roles": dataSourceUserRoles(),
"octopusdeploy_worker_pools": dataSourceWorkerPools(),
},
Expand Down
80 changes: 80 additions & 0 deletions octopusdeploy_framework/datasource_users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package octopusdeploy_framework

import (
"context"
"fmt"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/users"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"time"
)

type userDataSource struct {
*Config
}

type usersDataSourceModel struct {
ID types.String `tfsdk:"id"`
SpaceID types.String `tfsdk:"space_id"`
IDs types.List `tfsdk:"ids"`
Filter types.String `tfsdk:"filter"`
Skip types.Int64 `tfsdk:"skip"`
Take types.Int64 `tfsdk:"take"`
Users types.List `tfsdk:"users"`
}

func NewUsersDataSource() datasource.DataSource {
return &userDataSource{}
}

func (u *userDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = util.GetTypeName("users")
}

func (u *userDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schemas.UserSchema{}.GetDatasourceSchema()
}

func (u *userDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
u.Config = DataSourceConfiguration(req, resp)
}

func (u *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var err error
var data usersDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

query := users.UsersQuery{
IDs: util.GetIds(data.IDs),
Filter: data.Filter.ValueString(),
Skip: util.GetNumber(data.Skip),
Take: util.GetNumber(data.Take),
}

util.DatasourceReading(ctx, "users", query)

existingUsers, err := users.Get(u.Client, data.SpaceID.ValueString(), query)
if err != nil {
resp.Diagnostics.AddError("unable to load users", err.Error())
return
}

mappedUsers := []schemas.UserTypeResourceModel{}
tflog.Debug(ctx, fmt.Sprintf("users returned from API: %#v", existingUsers))
for _, user := range existingUsers.Items {
mappedUsers = append(mappedUsers, schemas.MapFromUser(user))
}

util.DatasourceResultCount(ctx, "users", len(mappedUsers))

data.Users, _ = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: schemas.UserObjectType()}, mappedUsers)
data.ID = types.StringValue("Users " + time.Now().UTC().String())

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
package octopusdeploy
package octopusdeploy_framework

import (
"fmt"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"testing"

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

func TestAccDataSourceUsers(t *testing.T) {
localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
name := fmt.Sprintf("data.octopusdeploy_users.%s", localName)
username := "d"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: ProtoV6ProviderFactories(),
PreCheck: func() { TestAccPreCheck(t) },
Steps: []resource.TestStep{
{
Config: testAccDataSourceUsersConfig(localName, username),
Check: resource.ComposeTestCheckFunc(
testAccCheckUsersDataSourceID(name),
resource.TestCheckResourceAttrSet(name, "users.#"),
)},
),
Config: testAccDataSourceUsersConfig(localName, username),
},
},
})
}
Expand Down
1 change: 1 addition & 0 deletions octopusdeploy_framework/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (p *octopusDeployFrameworkProvider) DataSources(ctx context.Context) []func
NewTagSetsDataSource,
NewScriptModuleDataSource,
NewTenantProjectDataSource,
NewUsersDataSource,
}
}

Expand Down
34 changes: 34 additions & 0 deletions octopusdeploy_framework/schemas/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,40 @@ func GetReadonlyDescriptionDatasourceSchema(resourceDescription string) datasour
}
}

func GetUsernameDatasourceSchema(isRequired bool) datasourceSchema.Attribute {
s := datasourceSchema.StringAttribute{
Description: "The username associated with this resource.",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
}

if isRequired {
s.Required = true
} else {
s.Optional = true
}

return s
}

func GetValueDatasourceSchema(isRequired bool) datasourceSchema.Attribute {
s := datasourceSchema.StringAttribute{
Description: "The value of this resource.",
Validators: []validator.String{
stringvalidator.LengthAtLeast(1),
},
}

if isRequired {
s.Required = true
} else {
s.Optional = true
}

return s
}

func GetIdResourceSchema() resourceSchema.Attribute {
return resourceSchema.StringAttribute{
Description: "The unique ID for this resource.",
Expand Down
Loading
Loading