Skip to content

Commit

Permalink
[datadog_team] Add datadog team data sources (#1946)
Browse files Browse the repository at this point in the history
* add teams datasources

* add tests

* update docs

* apply code review suggestions

* apply code review suggestions

* update codeowners
  • Loading branch information
skarimo authored Jun 5, 2023
1 parent 28a5560 commit 9bcdaf1
Show file tree
Hide file tree
Showing 15 changed files with 895 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ datadog/*datadog_service_level_objective* @DataDog/integrations-tools-and-librar
docs/resources/service_level_objective* @DataDog/integrations-tools-and-libraries @DataDog/slo-app @DataDog/documentation
datadog/*datadog_synthetics* @DataDog/integrations-tools-and-libraries @DataDog/synthetics-app
docs/resources/synthetics* @DataDog/integrations-tools-and-libraries @DataDog/synthetics-app @DataDog/documentation
datadog/*datadog_team* @DataDog/integrations-tools-and-libraries @DataDog/core-app
datadog/**/*datadog_team* @DataDog/integrations-tools-and-libraries @DataDog/core-app
docs/resources/team* @DataDog/integrations-tools-and-libraries @DataDog/core-app @DataDog/documentation
datadog/*datadog_timeboard* @DataDog/integrations-tools-and-libraries @DataDog/dashboards
docs/resources/timeboard* @DataDog/integrations-tools-and-libraries @DataDog/dashboards @DataDog/documentation
Expand Down
172 changes: 172 additions & 0 deletions datadog/fwprovider/data_source_datadog_team.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package fwprovider

import (
"context"

"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
)

var (
_ datasource.DataSource = &DatadogTeamDataSource{}
)

func NewDatadogTeamDataSource() datasource.DataSource {
return &DatadogTeamDataSource{}
}

type DatadogTeamDataSourceModel struct {
// Query Parameters
TeamID types.String `tfsdk:"team_id"`
FilterKeyword types.String `tfsdk:"filter_keyword"`
// Results
ID types.String `tfsdk:"id"`
Description types.String `tfsdk:"description"`
Handle types.String `tfsdk:"handle"`
LinkCount types.Int64 `tfsdk:"link_count"`
Summary types.String `tfsdk:"summary"`
UserCount types.Int64 `tfsdk:"user_count"`
Name types.String `tfsdk:"name"`
}

type DatadogTeamDataSource struct {
Api *datadogV2.TeamsApi
Auth context.Context
}

func (r *DatadogTeamDataSource) Configure(_ context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) {
if request.ProviderData == nil {
return
}

providerData, ok := request.ProviderData.(*FrameworkProvider)
if !ok {
response.Diagnostics.AddError("Unexpected Resource Configure Type", "")
return
}

r.Api = providerData.DatadogApiInstances.GetTeamsApiV2()
r.Auth = providerData.Auth
}

func (d *DatadogTeamDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "team"
}

func (d *DatadogTeamDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Use this data source to retrieve information about an existing Datadog team.",
Attributes: map[string]schema.Attribute{
// Datasource ID
"id": utils.ResourceIDAttribute(),
// Datasource Parameters
"team_id": schema.StringAttribute{
Description: "The team's identifier.",
Optional: true,
Computed: true,
},
"filter_keyword": schema.StringAttribute{
Description: "Search query. Can be team name, team handle, or email of team member.",
Optional: true,
},
// Computed values
"description": schema.StringAttribute{
Computed: true,
Description: "Free-form markdown description/content for the team's homepage.",
},
"handle": schema.StringAttribute{
Computed: true,
Description: "The team's handle.",
},
"link_count": schema.Int64Attribute{
Description: "The number of links belonging to the team.",
Computed: true,
},
"summary": schema.StringAttribute{
Description: "A brief summary of the team, derived from the `description`.",
Computed: true,
},
"user_count": schema.Int64Attribute{
Description: "The number of users belonging to the team.",
Computed: true,
},
"name": schema.StringAttribute{
Computed: true,
Description: "The name of the team.",
},
},
}

}

func (d *DatadogTeamDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state DatadogTeamDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

if !state.TeamID.IsNull() {
teamID := state.TeamID.ValueString()
ddResp, _, err := d.Api.GetTeam(d.Auth, teamID)
if err != nil {
resp.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error getting datadog team"))
return
}

d.updateState(&state, ddResp.Data)
} else if !state.FilterKeyword.IsNull() {
filterKeyword := state.FilterKeyword.ValueString()
optionalParams := datadogV2.ListTeamsOptionalParameters{
FilterKeyword: &filterKeyword,
}

ddResp, _, err := d.Api.ListTeams(d.Auth, optionalParams)
if err != nil {
resp.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error listing datadog teams"))
return
}

if len(ddResp.Data) > 1 {
resp.Diagnostics.AddError("filter keyword returned more than one result, use more specific search criteria", "")
return
}
if len(ddResp.Data) == 0 {
resp.Diagnostics.AddError("filter keyword returned no result", "")
return
}

d.updateStateFromListResponse(&state, &ddResp.Data[0])
}

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *DatadogTeamDataSource) updateState(state *DatadogTeamDataSourceModel, teamData *datadogV2.TeamData) {
state.ID = types.StringValue(teamData.GetId())
attributes := teamData.GetAttributes()

state.Description = types.StringValue(attributes.GetDescription())
state.Handle = types.StringValue(attributes.GetHandle())
state.LinkCount = types.Int64Value(int64(attributes.GetLinkCount()))
state.Name = types.StringValue(attributes.GetName())
state.UserCount = types.Int64Value(int64(attributes.GetUserCount()))
state.Summary = types.StringValue(attributes.GetSummary())
}

func (r *DatadogTeamDataSource) updateStateFromListResponse(state *DatadogTeamDataSourceModel, teamData *datadogV2.Team) {
state.ID = types.StringValue(teamData.GetId())
state.TeamID = types.StringValue(teamData.GetId())

attributes := teamData.GetAttributes()
state.Description = types.StringValue(attributes.GetDescription())
state.Handle = types.StringValue(attributes.GetHandle())
state.LinkCount = types.Int64Value(int64(attributes.GetLinkCount()))
state.Name = types.StringValue(attributes.GetName())
state.UserCount = types.Int64Value(int64(attributes.GetUserCount()))
state.Summary = types.StringValue(attributes.GetSummary())
}
146 changes: 146 additions & 0 deletions datadog/fwprovider/data_source_datadog_team_memberships.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package fwprovider

import (
"context"
"fmt"

"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
)

var (
_ datasource.DataSource = &DatadogTeamMembershipsDataSource{}
)

type DatadogTeamMembershipsDataSourceModel struct {
// Query Parameters
TeamID types.String `tfsdk:"team_id"`
FilterKeyword types.String `tfsdk:"filter_keyword"`
// Results
ID types.String `tfsdk:"id"`
TeamMemberships []*TeamMembershipModel `tfsdk:"team_memberships"`
}

func NewDatadogTeamMembershipsDataSource() datasource.DataSource {
return &DatadogTeamMembershipsDataSource{}
}

type DatadogTeamMembershipsDataSource struct {
Api *datadogV2.TeamsApi
Auth context.Context
}

func (r *DatadogTeamMembershipsDataSource) Configure(_ context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) {
if request.ProviderData == nil {
return
}

providerData, ok := request.ProviderData.(*FrameworkProvider)
if !ok {
response.Diagnostics.AddError("Unexpected Resource Configure Type", "")
return
}

r.Api = providerData.DatadogApiInstances.GetTeamsApiV2()
r.Auth = providerData.Auth
}

func (d *DatadogTeamMembershipsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "team_memberships"
}

func (d *DatadogTeamMembershipsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Use this data source to retrieve information about existing Datadog team memberships.",
Attributes: map[string]schema.Attribute{
// Datasource ID
"id": utils.ResourceIDAttribute(),
// Datasource Parameters
"team_id": schema.StringAttribute{
Description: "The team's identifier.",
Required: true,
},
"filter_keyword": schema.StringAttribute{
Description: "Search query, can be user email or name.",
Optional: true,
},
// Computed values
"team_memberships": schema.ListAttribute{
Computed: true,
Description: "List of team memberships.",
ElementType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"team_id": types.StringType,
"user_id": types.StringType,
"role": types.StringType,
"id": types.StringType,
},
},
},
},
}

}

func (d *DatadogTeamMembershipsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state DatadogTeamMembershipsDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

var optionalParams datadogV2.GetTeamMembershipsOptionalParameters
teamID := state.TeamID.ValueString()

if !state.FilterKeyword.IsNull() {
optionalParams.FilterKeyword = state.FilterKeyword.ValueStringPointer()
}

pageSize := int64(100)
pageNumber := int64(0)

var userTeams []datadogV2.UserTeam
for {
optionalParams.PageNumber = &pageNumber
optionalParams.PageSize = &pageSize

ddResp, _, err := d.Api.GetTeamMemberships(d.Auth, teamID, optionalParams)
if err != nil {
resp.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error getting team memberships"))
return
}

userTeams = append(userTeams, ddResp.GetData()...)
if len(ddResp.GetData()) < 100 {
break
}
pageNumber++
}

d.updateState(&state, &userTeams)

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func (r *DatadogTeamMembershipsDataSource) updateState(state *DatadogTeamMembershipsDataSourceModel, teamData *[]datadogV2.UserTeam) {
state.ID = types.StringValue(fmt.Sprintf("%s:%s", state.TeamID.ValueString(), state.FilterKeyword.ValueString()))

var teamMemberships []*TeamMembershipModel
for _, user := range *teamData {
membership := TeamMembershipModel{
ID: types.StringValue(user.GetId()),
TeamId: types.StringValue(state.TeamID.ValueString()),
UserId: types.StringValue(user.Relationships.User.Data.GetId()),
Role: types.StringValue(string(user.Attributes.GetRole())),
}

teamMemberships = append(teamMemberships, &membership)
}

state.TeamMemberships = teamMemberships
}
2 changes: 2 additions & 0 deletions datadog/fwprovider/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ var Resources = []func() resource.Resource{
var Datasources = []func() datasource.DataSource{
NewAPIKeyDataSource,
NewDatadogIntegrationAWSNamespaceRulesDatasource,
NewDatadogTeamDataSource,
NewDatadogTeamMembershipsDataSource,
NewHostsDataSource,
NewIPRangesDataSource,
NewSensitiveDataScannerGroupOrderDatasource,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2023-06-01T15:26:11.394314-04:00
Loading

0 comments on commit 9bcdaf1

Please sign in to comment.