-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bdf3026
commit dc8b1cf
Showing
6 changed files
with
452 additions
and
0 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
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,29 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
) | ||
|
||
// TeamsClient is a client for working with teams. | ||
type TeamsClient interface { | ||
List(ctx context.Context, names []string) ([]*Team, error) | ||
} | ||
|
||
// Team is a representation of an team. | ||
type Team struct { | ||
BaseModel | ||
Name string `json:"name"` | ||
Description string `json:"description"` | ||
} | ||
|
||
// TeamFilter defines the search filter payload | ||
// when searching for team by name. | ||
// example request payload: | ||
// {"teams": {"name": {"any_": ["test"]}}}. | ||
type TeamFilter struct { | ||
Teams struct { | ||
Name struct { | ||
Any []string `json:"any_"` | ||
} `json:"name"` | ||
} `json:"teams"` | ||
} |
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,73 @@ | ||
package client | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/google/uuid" | ||
"github.com/prefecthq/terraform-provider-prefect/internal/api" | ||
) | ||
|
||
var _ = api.TeamsClient(&TeamsClient{}) | ||
|
||
type TeamsClient struct { | ||
hc *http.Client | ||
apiKey string | ||
routePrefix string | ||
} | ||
|
||
// Teams is a factory that initializes and returns a TeamsClient. | ||
// | ||
//nolint:ireturn // required to support PrefectClient mocking | ||
func (c *Client) TeamsClient(accountID uuid.UUID) (api.TeamsClient, error) { | ||
if accountID == uuid.Nil { | ||
accountID = c.defaultAccountID | ||
} | ||
|
||
return &TeamsClient{ | ||
hc: c.hc, | ||
apiKey: c.apiKey, | ||
routePrefix: getAccountScopedURL(c.endpoint, accountID, "teams"), | ||
}, nil | ||
} | ||
|
||
// List returns a list of teams, based on the provided filter. | ||
func (c *TeamsClient) List(ctx context.Context, names []string) ([]*api.Team, error) { | ||
var buf bytes.Buffer | ||
filterQuery := api.TeamFilter{} | ||
filterQuery.Teams.Name.Any = names | ||
|
||
if err := json.NewEncoder(&buf).Encode(&filterQuery); err != nil { | ||
return nil, fmt.Errorf("failed to encode filter payload data: %w", err) | ||
} | ||
|
||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/filter", c.routePrefix), &buf) | ||
if err != nil { | ||
return nil, fmt.Errorf("error creating request: %w", err) | ||
} | ||
|
||
setDefaultHeaders(req, c.apiKey) | ||
|
||
resp, err := c.hc.Do(req) | ||
if err != nil { | ||
return nil, fmt.Errorf("http error: %w", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
errorBody, _ := io.ReadAll(resp.Body) | ||
|
||
return nil, fmt.Errorf("status code %s, error=%s", resp.Status, errorBody) | ||
} | ||
|
||
var teams []*api.Team | ||
if err := json.NewDecoder(resp.Body).Decode(&teams); err != nil { | ||
return nil, fmt.Errorf("failed to decode response: %w", err) | ||
} | ||
|
||
return teams, 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,155 @@ | ||
package datasources | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/datasource" | ||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/prefecthq/terraform-provider-prefect/internal/api" | ||
"github.com/prefecthq/terraform-provider-prefect/internal/provider/customtypes" | ||
"github.com/prefecthq/terraform-provider-prefect/internal/provider/helpers" | ||
) | ||
|
||
// Ensure the implementation satisfies the expected interfaces. | ||
var _ datasource.DataSource = &TeamDataSource{} | ||
var _ datasource.DataSourceWithConfigure = &TeamDataSource{} | ||
|
||
type TeamDataSource struct { | ||
client api.PrefectClient | ||
} | ||
|
||
type TeamDataSourceModel struct { | ||
ID customtypes.UUIDValue `tfsdk:"id"` | ||
Name types.String `tfsdk:"first_name"` | ||
Description types.String `tfsdk:"last_name"` | ||
|
||
AccountID customtypes.UUIDValue `tfsdk:"account_id"` | ||
} | ||
|
||
// NewTeamDataSource returns a new TeamDataSource. | ||
// | ||
//nolint:ireturn // required by Terraform API | ||
func NewTeamDataSource() datasource.DataSource { | ||
return &TeamDataSource{} | ||
} | ||
|
||
// Metadata returns the data source type name. | ||
func (d *TeamDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { | ||
resp.TypeName = req.ProviderTypeName + "_team" | ||
} | ||
|
||
// Shared set of schema attributes between team (singular) | ||
// and teams (plural) datasources. Any team (singular) | ||
// specific attributes will be added to a deep copy in the Schema method. | ||
var teamAttributesBase = map[string]schema.Attribute{ | ||
"id": schema.StringAttribute{ | ||
Computed: true, | ||
CustomType: customtypes.UUIDType{}, | ||
Description: "Team ID (UUID)", | ||
}, | ||
"name": schema.StringAttribute{ | ||
Computed: true, | ||
Description: "Name of Team", | ||
}, | ||
"description": schema.StringAttribute{ | ||
Computed: true, | ||
Description: "Description of team", | ||
}, | ||
} | ||
|
||
// Schema defines the schema for the data source. | ||
func (d *TeamDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { | ||
|
||
// Create a copy of the base attributes | ||
// and add the account ID overrides here | ||
// as they are not needed in the teams (plural) list | ||
teamAttributes := make(map[string]schema.Attribute) | ||
for k, v := range workPoolAttributesBase { | ||
teamAttributes[k] = v | ||
} | ||
teamAttributes["account_id"] = schema.StringAttribute{ | ||
CustomType: customtypes.UUIDType{}, | ||
Description: "Account ID (UUID), defaults to the account set in the provider", | ||
Optional: true, | ||
} | ||
|
||
resp.Schema = schema.Schema{ | ||
Description: ` | ||
Get information about an existing Team by their name. | ||
<br> | ||
Use this data source to obtain team IDs to manage Workspace Access. | ||
`, | ||
Attributes: teamAttributes, | ||
} | ||
} | ||
|
||
// Configure adds the provider-configured client to the data source. | ||
func (d *TeamDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { | ||
if req.ProviderData == nil { | ||
return | ||
} | ||
|
||
client, ok := req.ProviderData.(api.PrefectClient) | ||
if !ok { | ||
resp.Diagnostics.AddError( | ||
"Unexpected Data Source Configure Type", | ||
fmt.Sprintf("Expected api.PrefectClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), | ||
) | ||
|
||
return | ||
} | ||
|
||
d.client = client | ||
} | ||
|
||
// Read refreshes the Terraform state with the latest data. | ||
func (d *TeamDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { | ||
var config TeamDataSourceModel | ||
|
||
// Populate the model from data source configuration and emit diagnostics on error | ||
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
client, err := d.client.Teams(config.AccountID.ValueUUID()) | ||
if err != nil { | ||
resp.Diagnostics.Append(helpers.CreateClientErrorDiagnostic("Teams", err)) | ||
|
||
return | ||
} | ||
|
||
// Fetch an existing Team by name | ||
// Here, we'd expect only 1 Team (or none) to be returned | ||
// as we are querying a single Team name, not a list of names | ||
// teams, err := client.List(ctx, []string{model.Name.ValueString()}) | ||
teams, err := client.List(ctx, []string{config.Name.ValueString()}) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"Error refreshing Team state", | ||
fmt.Sprintf("Could not search for Team, unexpected error: %s", err.Error()), | ||
) | ||
} | ||
|
||
if len(teams) != 1 { | ||
resp.Diagnostics.AddError( | ||
"Could not find Team", | ||
fmt.Sprintf("Could not find Team with name %s", config.Name.ValueString()), | ||
) | ||
|
||
return | ||
} | ||
|
||
fetchedTeam := teams[0] | ||
|
||
config.ID = customtypes.NewUUIDValue(fetchedTeam.ID) | ||
config.Name = types.StringValue(fetchedTeam.Name) | ||
config.Description = types.StringValue(fetchedTeam.Description) | ||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
} |
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 @@ | ||
package datasources_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
"github.com/prefecthq/terraform-provider-prefect/internal/testutils" | ||
) | ||
|
||
func fixtureAccTeam(name string) string { | ||
return fmt.Sprintf(` | ||
data "prefect_team" "default" { | ||
name = "%s" | ||
} | ||
`, name) | ||
} | ||
|
||
//nolint:paralleltest // we use the resource.ParallelTest helper instead | ||
func TestAccDatasource_team(t *testing.T) { | ||
dataSourceName := "data.prefect_team.default" | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, | ||
PreCheck: func() { testutils.AccTestPreCheck(t) }, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: fixtureAccTeam("TEST_NAME"), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
resource.TestCheckResourceAttr(dataSourceName, "name", "TEST_NAME"), | ||
resource.TestCheckResourceAttrSet(dataSourceName, "id"), | ||
resource.TestCheckResourceAttrSet(dataSourceName, "description"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} |
Oops, something went wrong.