Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Resources:
- `forgejo_repository` ([documentation](docs/resources/repository.md))
- `forgejo_repository_action_secret` ([documentation](docs/resources/repository_action_secret.md))
- `forgejo_ssh_key` ([documentation](docs/resources/ssh_key.md))
- `forgejo_team` ([documentation](docs/resources/team.md))
- `forgejo_user` ([documentation](docs/resources/user.md))

Data Sources:
Expand All @@ -33,6 +34,7 @@ Data Sources:
- `forgejo_organization` ([documentation](docs/data-sources/organization.md))
- `forgejo_repository` ([documentation](docs/data-sources/repository.md))
- `forgejo_ssh_key` ([documentation](docs/data-sources/ssh_key.md))
- `forgejo_team` ([documentation](docs/data-sources/team.md))
- `forgejo_user` ([documentation](docs/data-sources/user.md))

## Using the Provider
Expand Down
30 changes: 30 additions & 0 deletions docs/data-sources/team.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "forgejo_team Data Source - forgejo"
subcategory: ""
description: |-
Forgejo team data source.
---

# forgejo_team (Data Source)

Forgejo team data source.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Name of the team.
- `organization_id` (Number) ID of the owning organization.

### Read-Only

- `can_create_org_repo` (Boolean) Can create repositories?
- `description` (String) Description of the team.
- `id` (Number) Numeric identifier of the team.
- `includes_all_repositories` (Boolean) Has access to all repositories?
- `permission` (String) Permissions within the owning organization.
- `units` (Set of String) Set of units.
33 changes: 33 additions & 0 deletions docs/resources/team.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "forgejo_team Resource - forgejo"
subcategory: ""
description: |-
Forgejo team resource.
---

# forgejo_team (Resource)

Forgejo team resource.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Name of the team.
- `organization_id` (Number) ID of the owning organization.

### Optional

- `can_create_org_repo` (Boolean) Can create repositories?
- `description` (String) Description of the team.
- `includes_all_repositories` (Boolean) Has access to all repositories?
- `permission` (String) Permissions within the owning organization. **Note**: If you set `admin` or `owner` here, make sure to set all units. This is due to an SDK limitation.
- `units` (Set of String) Set of units. **Note**: If the permission is `admin` or `owner` this should include all units due to an SDK limitation.

### Read-Only

- `id` (Number) Numeric identifier of the team.
34 changes: 34 additions & 0 deletions internal/provider/organization_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,37 @@ func (d *organizationDataSource) Read(ctx context.Context, req datasource.ReadRe
func NewOrganizationDataSource() datasource.DataSource {
return &organizationDataSource{}
}

// Use Forgejo client to get an organization by ID.
func getOrganizationByID(ctx context.Context, client *forgejo.Client, orgID types.Int64) (organization *forgejo.Organization, err error) {
tflog.Info(ctx, "Getting organization by its ID", map[string]any{
"organization_id": orgID,
})

organizations, resp, err := client.AdminListOrgs(forgejo.AdminListOrgsOptions{})
if err != nil {
tflog.Error(ctx, "Error", map[string]any{
"status": resp.Status,
})

switch resp.StatusCode {
case 403:
err = fmt.Errorf(
"not allowed to list organizations: %s",
err,
)
default:
err = fmt.Errorf("unknown error: %s", err)
}
return nil, err
}

for _, potentialOrganization := range organizations {
if orgID.Equal(types.Int64Value(potentialOrganization.ID)) {
organization = potentialOrganization
break
}
}

return organization, nil
}
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ func (p *forgejoProvider) DataSources(_ context.Context) []func() datasource.Dat
NewOrganizationDataSource,
NewRepositoryDataSource,
NewSSHKeyDataSource,
NewTeamDataSource,
NewUserDataSource,
}
}
Expand All @@ -261,6 +262,7 @@ func (p *forgejoProvider) Resources(_ context.Context) []func() resource.Resourc
NewRepositoryResource,
NewSSHKeyResource,
NewBranchProtectionResource,
NewTeamResource,
NewUserResource,
}
}
Expand Down
215 changes: 215 additions & 0 deletions internal/provider/team_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package provider

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/hashicorp/terraform-plugin-log/tflog"

"codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
)

// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &teamDataSource{}
_ datasource.DataSourceWithConfigure = &teamDataSource{}
)

// teamDataSource is the data source implementation.
type teamDataSource struct {
client *forgejo.Client
}

// teamDataSourceModel maps the data source schema data.
// https://pkg.go.dev/codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2#Team
type teamDataSourceModel struct {
ID types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
OrganizationID types.Int64 `tfsdk:"organization_id"`
CanCreateOrgRepo types.Bool `tfsdk:"can_create_org_repo"`
Description types.String `tfsdk:"description"`
IncludesAllRepositories types.Bool `tfsdk:"includes_all_repositories"`
Permission types.String `tfsdk:"permission"`
Units types.Set `tfsdk:"units"`
}

// Metadata returns the data source type name.
func (d *teamDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_team"
}

// Schema defines the schema for the data source.
func (d *teamDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Forgejo team data source.",

Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{
Description: "Numeric identifier of the team.",
Computed: true,
},
"name": schema.StringAttribute{
Description: "Name of the team.",
Required: true,
},
"organization_id": schema.Int64Attribute{
Description: "ID of the owning organization.",
Required: true,
},
"can_create_org_repo": schema.BoolAttribute{
Description: "Can create repositories?",
Computed: true,
},
"description": schema.StringAttribute{
Description: "Description of the team.",
Computed: true,
},
"includes_all_repositories": schema.BoolAttribute{
Description: "Has access to all repositories?",
Computed: true,
},
"permission": schema.StringAttribute{
Description: "Permissions within the owning organization.",
Computed: true,
},
"units": schema.SetAttribute{
Description: "Set of units.",
ElementType: types.StringType,
Computed: true,
},
},
}
}

// Configure adds the provider configured client to the data source.
func (d *teamDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*forgejo.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf(
"Expected *forgejo.Client, 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) {
defer un(trace(ctx, "Read team data source"))

var data teamDataSourceModel

// Read Terraform configuration data into model.
diags := req.Config.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

tflog.Info(ctx, "Get team by name", map[string]any{
"name": data.Name.ValueString(),
"organization_id": data.OrganizationID.ValueInt64(),
})

// Use Forgejo client to get team by name.
team, err := getOrgTeamByName(ctx, d.client, data.OrganizationID, data.Name)
if err != nil {
resp.Diagnostics.AddError("Unable to get team by name", err.Error())
return
}

if team == nil {
err = fmt.Errorf(
"no Team with name '%s' was found",
data.Name.String(),
)
resp.Diagnostics.AddError("Unable to get team by name", err.Error())
return
}

// Map response body to model.
data.ID = types.Int64Value(team.ID)
data.Name = types.StringValue(team.Name)
data.Description = types.StringValue(team.Description)
if team.Organization != nil {
data.OrganizationID = types.Int64Value(team.Organization.ID)
}
data.Permission = types.StringValue(string(team.Permission))
data.CanCreateOrgRepo = types.BoolValue(team.CanCreateOrgRepo)
data.IncludesAllRepositories = types.BoolValue(team.IncludesAllRepositories)
data.Units, diags = types.SetValueFrom(ctx, types.StringType, team.Units)

resp.Diagnostics.Append(diags...)

// Save data into Terraform state.
diags = resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
}

// NewTeamDataSource is a helper function to simplify the provider implementation.
func NewTeamDataSource() datasource.DataSource {
return &teamDataSource{}
}

// Use Forgejo client to get team by name.
func getOrgTeamByName(ctx context.Context, client *forgejo.Client, orgID types.Int64, teamName types.String) (team *forgejo.Team, err error) {
tflog.Info(ctx, "Getting team in org", map[string]any{
"team": teamName,
"organization_id": orgID,
})

organization, err := getOrganizationByID(ctx, client, orgID)
if err != nil {
return nil, err
}

if organization == nil {
err = fmt.Errorf(
"no Organization with id '%d' was found",
orgID.ValueInt64(),
)
return nil, err
}

teams, resp, err := client.ListOrgTeams(organization.UserName, forgejo.ListTeamsOptions{})
if err != nil {
tflog.Error(ctx, "Error", map[string]any{
"status": resp.Status,
})

switch resp.StatusCode {
case 404:
err = fmt.Errorf(
"the Organization with name '%s' was not found: %s",
organization.UserName,
err,
)
default:
err = fmt.Errorf("unknown error: %s", err)
}
return nil, err
}

for _, potentialTeam := range teams {
if teamName.Equal(types.StringValue(potentialTeam.Name)) {
team = potentialTeam
break
}
}

return team, nil
}
Loading