teamcity/group_data_source.go (167 lines of code) (raw):
package teamcity
import (
"context"
"terraform-provider-teamcity/client"
"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"
)
var (
_ datasource.DataSource = &groupDataSource{}
_ datasource.DataSourceWithConfigure = &groupDataSource{}
)
type groupDataSource struct {
client *client.Client
}
func NewGroupDataSource() datasource.DataSource {
return &groupDataSource{}
}
func (d *groupDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_group"
}
type groupDataSourceModel struct {
Id types.String `tfsdk:"id"`
Key types.String `tfsdk:"key"`
Name types.String `tfsdk:"name"`
Roles []roleAssignment `tfsdk:"roles"`
ParentGroups types.Set `tfsdk:"parent_groups"`
}
func (d *groupDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Use this data source to get information about an existing TeamCity group.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "The internal ID of the group.",
},
"key": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The key (identifier) of the group to retrieve. Either key or name must be specified.",
},
"name": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "The name of the group to retrieve. Either key or name must be specified.",
},
"roles": schema.SetNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
Description: "The role ID.",
},
"global": schema.BoolAttribute{
Computed: true,
Description: "Whether the role is assigned globally.",
},
"project": schema.StringAttribute{
Computed: true,
Description: "The project ID if the role is project-specific.",
},
},
},
Description: "List of roles assigned to the group.",
},
"parent_groups": schema.SetAttribute{
ElementType: types.StringType,
Computed: true,
Description: "Set of parent group keys.",
},
},
}
}
func (d *groupDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
d.client = req.ProviderData.(*client.Client)
}
func (d *groupDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config groupDataSourceModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Validate that either key or name is specified
if config.Key.IsNull() && config.Name.IsNull() {
resp.Diagnostics.AddError(
"Invalid configuration",
"Either 'key' or 'name' must be specified",
)
return
}
// If both are specified, prefer key
if !config.Key.IsNull() && !config.Name.IsNull() {
resp.Diagnostics.AddWarning(
"Both key and name specified",
"Both 'key' and 'name' were specified. Using 'key' for the lookup.",
)
}
var group *client.Group
var err error
// Get group by key or name
if !config.Key.IsNull() {
// Get by key
group, err = d.client.GetGroup(config.Key.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error reading group by key",
err.Error(),
)
return
}
} else if !config.Name.IsNull() {
// Get by name - this requires GetGroupByName method in client
// If the client doesn't have this method, we might need to get all groups and filter
group, err = d.client.GetGroupByName(config.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error reading group by name",
err.Error(),
)
return
}
}
if group == nil {
identifier := config.Key.ValueString()
if config.Key.IsNull() {
identifier = "name '" + config.Name.ValueString() + "'"
} else {
identifier = "key '" + identifier + "'"
}
resp.Diagnostics.AddError(
"Group not found",
"The group with "+identifier+" was not found",
)
return
}
// Map response to model
state := groupDataSourceModel{
Id: types.StringValue(group.Key),
Key: types.StringValue(group.Key),
Name: types.StringValue(group.Name),
}
// Map roles
if group.Roles != nil && len(group.Roles.RoleAssignment) > 0 {
state.Roles = []roleAssignment{}
for _, role := range group.Roles.RoleAssignment {
assignment := roleAssignment{
Id: types.StringValue(role.Id),
}
if role.Scope == "g" {
assignment.Global = types.BoolValue(true)
} else {
assignment.Global = types.BoolValue(false)
assignment.Project = types.StringValue(role.Scope[2:])
}
state.Roles = append(state.Roles, assignment)
}
}
// Map parent groups
if group.Parents != nil && len(group.Parents.Group) > 0 {
var parentKeys []attr.Value
for _, parent := range group.Parents.Group {
parentKeys = append(parentKeys, types.StringValue(parent.Key))
}
state.ParentGroups, _ = types.SetValue(types.StringType, parentKeys)
} else {
state.ParentGroups = types.SetNull(types.StringType)
}
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
}