Skip to content

Commit

Permalink
[After Twingate#443] Feature: add filtering for service accounts data…
Browse files Browse the repository at this point in the history
…source (Twingate#445)

* added new attributes for resources datasource

* added optional name attributes for resources datasource

* added feature branch

* fix test

* remove feature branch

* enable tests

* remove feature branch

* added filtering by name for service_accounts datasource

* remove feature branch

* refactore

* updated docs

* updated docs

* update resources datasource: allow to list all resources

* fix docs

---------

Co-authored-by: bertekintw <[email protected]>
  • Loading branch information
vmanilo and bertekintw authored Feb 20, 2024
1 parent cad0a46 commit 6d55782
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 14 deletions.
12 changes: 11 additions & 1 deletion docs/data-sources/service_accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Service Accounts offer a way to provide programmatic, centrally-controlled, and
```terraform
data "twingate_service_accounts" "foo" {
name = "<your service account's name>"
# name_regexp = "<regular expression of service account name>"
# name_contains = "<a string in the service account name>"
# name_exclude = "<your service account's name to exclude>"
# name_prefix = "<prefix of service account name>"
# name_suffix = "<suffix of service account name>"
}
```

Expand All @@ -23,7 +28,12 @@ data "twingate_service_accounts" "foo" {

### Optional

- `name` (String) Filter results by the name of the Service Account.
- `name` (String) Returns only service accounts that exactly match this name. If no options are passed it will return all service accounts. Only one option can be used at a time.
- `name_contains` (String) Match when the value exist in the name of the service account.
- `name_exclude` (String) Match when the value does not exist in the name of the service account.
- `name_prefix` (String) The name of the service account must start with the value.
- `name_regexp` (String) The regular expression match of the name of the service account.
- `name_suffix` (String) The name of the service account must end with the value.

### Read-Only

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
data "twingate_service_accounts" "foo" {
name = "<your service account's name>"
# name_regexp = "<regular expression of service account name>"
# name_contains = "<a string in the service account name>"
# name_exclude = "<your service account's name to exclude>"
# name_prefix = "<prefix of service account name>"
# name_suffix = "<suffix of service account name>"
}
12 changes: 3 additions & 9 deletions twingate/internal/client/query/service-accounts-read.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,15 @@ func IsGqlKeyActive(item *GqlKeyIDEdge) bool {
}

type ServiceAccountFilterInput struct {
Name StringFilter `json:"name"`
Name *StringFilterOperationInput `json:"name"`
}

type StringFilter struct {
Eq string `json:"eq"`
}

func NewServiceAccountFilterInput(name string) *ServiceAccountFilterInput {
func NewServiceAccountFilterInput(name, filter string) *ServiceAccountFilterInput {
if name == "" {
return nil
}

return &ServiceAccountFilterInput{
Name: StringFilter{
Eq: name,
},
Name: NewStringFilterOperationInput(name, filter),
}
}
8 changes: 6 additions & 2 deletions twingate/internal/client/service-account.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,17 @@ func (client *Client) readServiceAccountsAfter(ctx context.Context, variables ma
func (client *Client) ReadServiceAccounts(ctx context.Context, input ...string) ([]*model.ServiceAccount, error) {
opr := resourceServiceAccount.read()

var name string
var name, filter string
if len(input) > 0 {
name = input[0]
}

if len(input) > 1 {
filter = input[1]
}

variables := newVars(
gqlNullable(query.NewServiceAccountFilterInput(name), "filter"),
gqlNullable(query.NewServiceAccountFilterInput(name, filter), "filter"),
cursor(query.CursorServices),
cursor(query.CursorResources),
cursor(query.CursorServiceKeys),
Expand Down
69 changes: 67 additions & 2 deletions twingate/internal/provider/datasource/service-accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
)

var ErrServiceAccountsDatasourceShouldSetOneOptionalNameAttribute = errors.New("Only one of name, name_regex, name_contains, name_exclude, name_prefix or name_suffix must be set.")

// Ensure the implementation satisfies the desired interfaces.
var _ datasource.DataSource = &serviceAccounts{}

Expand All @@ -26,6 +28,11 @@ type serviceAccounts struct {
type serviceAccountsModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
NameRegexp types.String `tfsdk:"name_regexp"`
NameContains types.String `tfsdk:"name_contains"`
NameExclude types.String `tfsdk:"name_exclude"`
NamePrefix types.String `tfsdk:"name_prefix"`
NameSuffix types.String `tfsdk:"name_suffix"`
ServiceAccounts []serviceAccountModel `tfsdk:"service_accounts"`
}

Expand Down Expand Up @@ -68,7 +75,27 @@ func (d *serviceAccounts) Schema(ctx context.Context, req datasource.SchemaReque
},
attr.Name: schema.StringAttribute{
Optional: true,
Description: "Filter results by the name of the Service Account.",
Description: "Returns only service accounts that exactly match this name. If no options are passed it will return all service accounts. Only one option can be used at a time.",
},
attr.Name + attr.FilterByRegexp: schema.StringAttribute{
Optional: true,
Description: "The regular expression match of the name of the service account.",
},
attr.Name + attr.FilterByContains: schema.StringAttribute{
Optional: true,
Description: "Match when the value exist in the name of the service account.",
},
attr.Name + attr.FilterByExclude: schema.StringAttribute{
Optional: true,
Description: "Match when the value does not exist in the name of the service account.",
},
attr.Name + attr.FilterByPrefix: schema.StringAttribute{
Optional: true,
Description: "The name of the service account must start with the value.",
},
attr.Name + attr.FilterBySuffix: schema.StringAttribute{
Optional: true,
Description: "The name of the service account must end with the value.",
},
attr.ServiceAccounts: schema.ListNestedAttribute{
Computed: true,
Expand Down Expand Up @@ -100,6 +127,7 @@ func (d *serviceAccounts) Schema(ctx context.Context, req datasource.SchemaReque
}
}

//nolint:cyclop
func (d *serviceAccounts) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data serviceAccountsModel

Expand All @@ -110,7 +138,44 @@ func (d *serviceAccounts) Read(ctx context.Context, req datasource.ReadRequest,
return
}

accounts, err := d.client.ReadServiceAccounts(ctx, data.Name.ValueString())
var name, filter string

if data.Name.ValueString() != "" {
name = data.Name.ValueString()
}

if data.NameRegexp.ValueString() != "" {
name = data.NameRegexp.ValueString()
filter = attr.FilterByRegexp
}

if data.NameContains.ValueString() != "" {
name = data.NameContains.ValueString()
filter = attr.FilterByContains
}

if data.NameExclude.ValueString() != "" {
name = data.NameExclude.ValueString()
filter = attr.FilterByExclude
}

if data.NamePrefix.ValueString() != "" {
name = data.NamePrefix.ValueString()
filter = attr.FilterByPrefix
}

if data.NameSuffix.ValueString() != "" {
name = data.NameSuffix.ValueString()
filter = attr.FilterBySuffix
}

if countOptionalAttributes(data.Name, data.NameRegexp, data.NameContains, data.NameExclude, data.NamePrefix, data.NameSuffix) > 1 {
addErr(&resp.Diagnostics, ErrServiceAccountsDatasourceShouldSetOneOptionalNameAttribute, TwingateResources)

return
}

accounts, err := d.client.ReadServiceAccounts(ctx, name, filter)
if err != nil && !errors.Is(err, client.ErrGraphqlResultIsEmpty) {
addErr(&resp.Diagnostics, err, TwingateServiceAccounts)

Expand Down
Loading

0 comments on commit 6d55782

Please sign in to comment.