-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP * updated project layout * fix linter * ignore sec warn * fix: re-generated docs * fix users test * added connector model * fix convertion graphql.ID to string * added connector tokens model * added group model * added remote-network model * fix fmt * added user model * fix fmt * WIP * added connectors pages * added featching all pages for groups * added featching all pages for resources * added featching all pages for users * updated test results location * wip: fixing tests * added generic paginated resource * added tests * renamed transport pkg to client * fixed tests * fix path to generated test coverage report * fix read resources * remove parallel resource tests * added debug log * debug error * debug error * fix test * remove logs * revert changes in ci.yml * simplify converters * added tests * added tests for models * fix fmt * added test coverage * added test coverage * added test coverage * fix test * added test coverage * run acc test * revert changes * added service-account resource * refactor acc tests * Fix http_max_retry doc * regenerated docs * renamed resource: service-account -> service * fix acctest * renamed resource to twingate_service_account * added test coverage * added test coverage * updated doc description Co-authored-by: Eran Kampf <205185+ekampf@users.noreply.github.com>
- Loading branch information
Showing
15 changed files
with
1,296 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "twingate_service_account Resource - terraform-provider-twingate" | ||
subcategory: "" | ||
description: |- | ||
Service Accounts offer a way to provide programmatic, centrally-controlled, and consistent access controls. | ||
--- | ||
|
||
# twingate_service_account (Resource) | ||
|
||
Service Accounts offer a way to provide programmatic, centrally-controlled, and consistent access controls. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
provider "twingate" { | ||
api_token = "1234567890abcdef" | ||
network = "mynetwork" | ||
} | ||
resource "twingate_service_account" "github_actions_prod" { | ||
name = "Github Actions PROD" | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `name` (String) The name of the Service Account in Twingate | ||
|
||
### Read-Only | ||
|
||
- `id` (String) Autogenerated ID of the Service Account | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
provider "twingate" { | ||
api_token = "1234567890abcdef" | ||
network = "mynetwork" | ||
} | ||
|
||
resource "twingate_service_account" "github_actions_prod" { | ||
name = "Github Actions PROD" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/twingate/go-graphql-client" | ||
) | ||
|
||
func TestPagination(t *testing.T) { | ||
badError := errors.New("bad error") | ||
|
||
cases := []struct { | ||
resource *PaginatedResource[int] | ||
nextPage nextPageFunc[int] | ||
|
||
expected *PaginatedResource[int] | ||
expectedErr error | ||
}{ | ||
{}, | ||
{ | ||
resource: &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: false, | ||
}, | ||
}, | ||
expected: &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: false, | ||
}, | ||
}, | ||
}, | ||
{ | ||
resource: &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: true, | ||
}, | ||
}, | ||
nextPage: func(ctx context.Context, variables map[string]interface{}, cursor graphql.String) (*PaginatedResource[int], error) { | ||
return nil, badError | ||
}, | ||
expected: &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: true, | ||
}, | ||
}, | ||
expectedErr: badError, | ||
}, | ||
{ | ||
resource: &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: true, | ||
}, | ||
Edges: []int{1, 2}, | ||
}, | ||
nextPage: func(ctx context.Context, variables map[string]interface{}, cursor graphql.String) (*PaginatedResource[int], error) { | ||
return &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: false, | ||
}, | ||
Edges: []int{3, 4}, | ||
}, nil | ||
}, | ||
expected: &PaginatedResource[int]{ | ||
PageInfo: PageInfo{ | ||
HasNextPage: true, | ||
}, | ||
Edges: []int{1, 2, 3, 4}, | ||
}, | ||
}, | ||
} | ||
|
||
for n, c := range cases { | ||
t.Run(fmt.Sprintf("case_%d", n), func(t *testing.T) { | ||
err := c.resource.fetchPages(context.TODO(), c.nextPage, map[string]interface{}{}) | ||
|
||
assert.Equal(t, c.expected, c.resource) | ||
assert.Equal(t, c.expectedErr, err) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/Twingate/terraform-provider-twingate/twingate/internal/model" | ||
"github.com/twingate/go-graphql-client" | ||
) | ||
|
||
const serviceAccountResourceName = "service account" | ||
|
||
type gqlServiceAccount struct { | ||
IDName | ||
} | ||
|
||
type createServiceAccountQuery struct { | ||
ServiceAccountCreate struct { | ||
Entity IDName | ||
OkError | ||
} `graphql:"serviceAccountCreate(name: $name)"` | ||
} | ||
|
||
func (client *Client) CreateServiceAccount(ctx context.Context, serviceAccountName string) (*model.ServiceAccount, error) { | ||
if serviceAccountName == "" { | ||
return nil, NewAPIError(ErrGraphqlNameIsEmpty, "create", serviceAccountResourceName) | ||
} | ||
|
||
variables := newVars(gqlField(serviceAccountName, "name")) | ||
response := createServiceAccountQuery{} | ||
|
||
err := client.GraphqlClient.NamedMutate(ctx, "createServiceAccount", &response, variables) | ||
if err != nil { | ||
return nil, NewAPIError(err, "create", serviceAccountResourceName) | ||
} | ||
|
||
if !response.ServiceAccountCreate.Ok { | ||
message := response.ServiceAccountCreate.Error | ||
|
||
return nil, NewAPIError(NewMutationError(message), "create", serviceAccountResourceName) | ||
} | ||
|
||
return response.ToModel(), nil | ||
} | ||
|
||
type readServiceAccountQuery struct { | ||
ServiceAccount *gqlServiceAccount `graphql:"serviceAccount(id: $id)"` | ||
} | ||
|
||
func (client *Client) ReadServiceAccount(ctx context.Context, serviceAccountID string) (*model.ServiceAccount, error) { | ||
if serviceAccountID == "" { | ||
return nil, NewAPIError(ErrGraphqlIDIsEmpty, "read", serviceAccountResourceName) | ||
} | ||
|
||
variables := newVars(gqlID(serviceAccountID)) | ||
response := readServiceAccountQuery{} | ||
|
||
err := client.GraphqlClient.NamedQuery(ctx, "readServiceAccount", &response, variables) | ||
if err != nil { | ||
return nil, NewAPIErrorWithID(err, "read", serviceAccountResourceName, serviceAccountID) | ||
} | ||
|
||
if response.ServiceAccount == nil { | ||
return nil, NewAPIErrorWithID(ErrGraphqlResultIsEmpty, "read", serviceAccountResourceName, serviceAccountID) | ||
} | ||
|
||
return response.ToModel(), nil | ||
} | ||
|
||
type updateServiceAccountQuery struct { | ||
ServiceAccountUpdate struct { | ||
Entity *gqlServiceAccount | ||
OkError | ||
} `graphql:"serviceAccountUpdate(id: $id, name: $name)"` | ||
} | ||
|
||
func (client *Client) UpdateServiceAccount(ctx context.Context, serviceAccount *model.ServiceAccount) (*model.ServiceAccount, error) { | ||
if serviceAccount == nil || serviceAccount.ID == "" { | ||
return nil, NewAPIError(ErrGraphqlIDIsEmpty, "update", serviceAccountResourceName) | ||
} | ||
|
||
if serviceAccount.Name == "" { | ||
return nil, NewAPIError(ErrGraphqlNameIsEmpty, "update", serviceAccountResourceName) | ||
} | ||
|
||
variables := newVars( | ||
gqlID(serviceAccount.ID), | ||
gqlField(serviceAccount.Name, "name"), | ||
) | ||
|
||
response := updateServiceAccountQuery{} | ||
|
||
err := client.GraphqlClient.NamedMutate(ctx, "updateServiceAccount", &response, variables) | ||
if err != nil { | ||
return nil, NewAPIErrorWithID(err, "update", serviceAccountResourceName, serviceAccount.ID) | ||
} | ||
|
||
if !response.ServiceAccountUpdate.Ok { | ||
return nil, NewAPIErrorWithID(NewMutationError(response.ServiceAccountUpdate.Error), "update", serviceAccountResourceName, serviceAccount.ID) | ||
} | ||
|
||
if response.ServiceAccountUpdate.Entity == nil { | ||
return nil, NewAPIErrorWithID(ErrGraphqlResultIsEmpty, "update", serviceAccountResourceName, serviceAccount.ID) | ||
} | ||
|
||
return response.ServiceAccountUpdate.Entity.ToModel(), nil | ||
} | ||
|
||
type deleteServiceAccountQuery struct { | ||
ServiceAccountDelete *OkError `graphql:"serviceAccountDelete(id: $id)"` | ||
} | ||
|
||
func (client *Client) DeleteServiceAccount(ctx context.Context, serviceAccountID string) error { | ||
if serviceAccountID == "" { | ||
return NewAPIError(ErrGraphqlIDIsEmpty, "delete", serviceAccountResourceName) | ||
} | ||
|
||
variables := newVars(gqlID(serviceAccountID)) | ||
response := deleteServiceAccountQuery{} | ||
|
||
err := client.GraphqlClient.NamedMutate(ctx, "deleteServiceAccount", &response, variables) | ||
if err != nil { | ||
return NewAPIErrorWithID(err, "delete", serviceAccountResourceName, serviceAccountID) | ||
} | ||
|
||
if !response.ServiceAccountDelete.Ok { | ||
return NewAPIErrorWithID(NewMutationError(response.ServiceAccountDelete.Error), "delete", serviceAccountResourceName, serviceAccountID) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type ServiceAccountEdge struct { | ||
Node *gqlServiceAccount | ||
} | ||
|
||
type ServiceAccounts struct { | ||
PaginatedResource[*ServiceAccountEdge] | ||
} | ||
|
||
type readServiceAccountsQuery struct { | ||
ServiceAccounts ServiceAccounts | ||
} | ||
|
||
func (client *Client) ReadServiceAccounts(ctx context.Context) ([]*model.ServiceAccount, error) { | ||
response := readServiceAccountsQuery{} | ||
|
||
err := client.GraphqlClient.NamedQuery(ctx, "readServiceAccounts", &response, nil) | ||
if err != nil { | ||
return nil, NewAPIErrorWithID(err, "read", serviceAccountResourceName, "All") | ||
} | ||
|
||
if len(response.ServiceAccounts.Edges) == 0 { | ||
return nil, NewAPIErrorWithID(ErrGraphqlResultIsEmpty, "read", serviceAccountResourceName, "All") | ||
} | ||
|
||
err = response.ServiceAccounts.fetchPages(ctx, client.readServiceAccountsAfter, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return response.ServiceAccounts.ToModel(), nil | ||
} | ||
|
||
type readServiceAccountsAfter struct { | ||
ServiceAccounts ServiceAccounts `graphql:"serviceAccounts(after: $serviceAccountsEndCursor)"` | ||
} | ||
|
||
func (client *Client) readServiceAccountsAfter(ctx context.Context, variables map[string]interface{}, cursor graphql.String) (*PaginatedResource[*ServiceAccountEdge], error) { | ||
if variables == nil { | ||
variables = make(map[string]interface{}) | ||
} | ||
|
||
variables["serviceAccountsEndCursor"] = cursor | ||
response := readServiceAccountsAfter{} | ||
|
||
err := client.GraphqlClient.NamedQuery(ctx, "readServiceAccounts", &response, variables) | ||
if err != nil { | ||
return nil, NewAPIErrorWithID(err, "read", serviceAccountResourceName, "All") | ||
} | ||
|
||
if len(response.ServiceAccounts.Edges) == 0 { | ||
return nil, NewAPIErrorWithID(ErrGraphqlResultIsEmpty, "read", serviceAccountResourceName, "All") | ||
} | ||
|
||
return &response.ServiceAccounts.PaginatedResource, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package model | ||
|
||
type ServiceAccount struct { | ||
ID string | ||
Name string | ||
} | ||
|
||
func (s ServiceAccount) GetName() string { | ||
return s.Name | ||
} | ||
|
||
func (s ServiceAccount) GetID() string { | ||
return s.ID | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.