Skip to content

Commit c68daae

Browse files
committed
feat: Add support for managing a team.
1 parent 15a5cbf commit c68daae

File tree

9 files changed

+1040
-0
lines changed

9 files changed

+1040
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Resources:
2222
- `forgejo_repository` ([documentation](docs/resources/repository.md))
2323
- `forgejo_repository_action_secret` ([documentation](docs/resources/repository_action_secret.md))
2424
- `forgejo_ssh_key` ([documentation](docs/resources/ssh_key.md))
25+
- `forgejo_team` ([documentation](docs/resources/team.md))
2526
- `forgejo_user` ([documentation](docs/resources/user.md))
2627

2728
Data Sources:
@@ -31,6 +32,7 @@ Data Sources:
3132
- `forgejo_organization` ([documentation](docs/data-sources/organization.md))
3233
- `forgejo_repository` ([documentation](docs/data-sources/repository.md))
3334
- `forgejo_ssh_key` ([documentation](docs/data-sources/ssh_key.md))
35+
- `forgejo_team` ([documentation](docs/data-sources/team.md))
3436
- `forgejo_user` ([documentation](docs/data-sources/user.md))
3537

3638
## Using the Provider

docs/data-sources/team.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "forgejo_team Data Source - forgejo"
4+
subcategory: ""
5+
description: |-
6+
Forgejo team data source.
7+
---
8+
9+
# forgejo_team (Data Source)
10+
11+
Forgejo team data source.
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `name` (String) Name of the team.
21+
- `organization_id` (Number) ID of the owning organization.
22+
23+
### Read-Only
24+
25+
- `can_create_org_repo` (Boolean) Can create repositories?
26+
- `description` (String) Description of the organization.
27+
- `id` (Number) Numeric identifier of the team.
28+
- `includes_all_repositories` (Boolean) Has access to all repositories?
29+
- `permission` (String) Organization permission.
30+
- `units` (Set of String) Set of units.

docs/resources/team.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "forgejo_team Resource - forgejo"
4+
subcategory: ""
5+
description: |-
6+
Forgejo team resource.
7+
---
8+
9+
# forgejo_team (Resource)
10+
11+
Forgejo team resource.
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `name` (String) Name of the team.
21+
- `organization_id` (Number) ID of the owning organization.
22+
23+
### Optional
24+
25+
- `can_create_org_repo` (Boolean) Can create repositories?
26+
- `description` (String) Description of the team.
27+
- `import_if_exists` (Boolean) Import the team if it already exists?
28+
- `includes_all_repositories` (Boolean) Has access to all repositories?
29+
- `permission` (String) Organization permission.
30+
- `units` (Set of String) Set of units.
31+
32+
### Read-Only
33+
34+
- `id` (Number) Numeric identifier of the team.

internal/provider/organization_data_source.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,37 @@ func (d *organizationDataSource) Read(ctx context.Context, req datasource.ReadRe
169169
func NewOrganizationDataSource() datasource.DataSource {
170170
return &organizationDataSource{}
171171
}
172+
173+
// Use Forgejo client to get an organization by ID.
174+
func getOrganizationByID(ctx context.Context, client *forgejo.Client, orgID types.Int64) (organization *forgejo.Organization, err error) {
175+
tflog.Info(ctx, "Getting organization by its ID", map[string]any{
176+
"organization_id": orgID,
177+
})
178+
179+
organizations, resp, err := client.AdminListOrgs(forgejo.AdminListOrgsOptions{})
180+
if err != nil {
181+
tflog.Error(ctx, "Error", map[string]any{
182+
"status": resp.Status,
183+
})
184+
185+
switch resp.StatusCode {
186+
case 403:
187+
err = fmt.Errorf(
188+
"not allowed to list organizations: %s",
189+
err,
190+
)
191+
default:
192+
err = fmt.Errorf("unknown error: %s", err)
193+
}
194+
return nil, err
195+
}
196+
197+
for _, potentialOrganization := range organizations {
198+
if orgID.Equal(types.Int64Value(potentialOrganization.ID)) {
199+
organization = potentialOrganization
200+
break
201+
}
202+
}
203+
204+
return organization, nil
205+
}

internal/provider/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ func (p *forgejoProvider) DataSources(_ context.Context) []func() datasource.Dat
244244
NewOrganizationDataSource,
245245
NewRepositoryDataSource,
246246
NewSSHKeyDataSource,
247+
NewTeamDataSource,
247248
NewUserDataSource,
248249
}
249250
}
@@ -259,6 +260,7 @@ func (p *forgejoProvider) Resources(_ context.Context) []func() resource.Resourc
259260
NewRepositoryResource,
260261
NewSSHKeyResource,
261262
NewBranchProtectionResource,
263+
NewTeamResource,
262264
NewUserResource,
263265
}
264266
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/datasource"
8+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
"github.com/hashicorp/terraform-plugin-log/tflog"
11+
12+
"codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
13+
)
14+
15+
// Ensure the implementation satisfies the expected interfaces.
16+
var (
17+
_ datasource.DataSource = &teamDataSource{}
18+
_ datasource.DataSourceWithConfigure = &teamDataSource{}
19+
)
20+
21+
// teamDataSource is the data source implementation.
22+
type teamDataSource struct {
23+
client *forgejo.Client
24+
}
25+
26+
// teamDataSourceModel maps the data source schema data.
27+
// https://pkg.go.dev/codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2#Team
28+
type teamDataSourceModel struct {
29+
ID types.Int64 `tfsdk:"id"`
30+
Name types.String `tfsdk:"name"`
31+
OrganizationID types.Int64 `tfsdk:"organization_id"`
32+
CanCreateOrgRepo types.Bool `tfsdk:"can_create_org_repo"`
33+
Description types.String `tfsdk:"description"`
34+
IncludesAllRepositories types.Bool `tfsdk:"includes_all_repositories"`
35+
Permission types.String `tfsdk:"permission"`
36+
Units types.Set `tfsdk:"units"`
37+
}
38+
39+
// Metadata returns the data source type name.
40+
func (d *teamDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
41+
resp.TypeName = req.ProviderTypeName + "_team"
42+
}
43+
44+
// Schema defines the schema for the data source.
45+
func (d *teamDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
46+
resp.Schema = schema.Schema{
47+
Description: "Forgejo team data source.",
48+
49+
Attributes: map[string]schema.Attribute{
50+
"id": schema.Int64Attribute{
51+
Description: "Numeric identifier of the team.",
52+
Computed: true,
53+
},
54+
"name": schema.StringAttribute{
55+
Description: "Name of the team.",
56+
Required: true,
57+
},
58+
"organization_id": schema.Int64Attribute{
59+
Description: "ID of the owning organization.",
60+
Required: true,
61+
},
62+
"can_create_org_repo": schema.BoolAttribute{
63+
Description: "Can create repositories?",
64+
Computed: true,
65+
},
66+
"description": schema.StringAttribute{
67+
Description: "Description of the organization.",
68+
Computed: true,
69+
},
70+
"includes_all_repositories": schema.BoolAttribute{
71+
Description: "Has access to all repositories?",
72+
Computed: true,
73+
},
74+
"permission": schema.StringAttribute{
75+
Description: "Organization permission.",
76+
Computed: true,
77+
},
78+
"units": schema.SetAttribute{
79+
Description: "Set of units.",
80+
ElementType: types.StringType,
81+
Computed: true,
82+
},
83+
},
84+
}
85+
}
86+
87+
// Configure adds the provider configured client to the data source.
88+
func (d *teamDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
89+
// Prevent panic if the provider has not been configured.
90+
if req.ProviderData == nil {
91+
return
92+
}
93+
94+
client, ok := req.ProviderData.(*forgejo.Client)
95+
if !ok {
96+
resp.Diagnostics.AddError(
97+
"Unexpected Data Source Configure Type",
98+
fmt.Sprintf(
99+
"Expected *forgejo.Client, got: %T. Please report this issue to the provider developers.",
100+
req.ProviderData,
101+
),
102+
)
103+
104+
return
105+
}
106+
107+
d.client = client
108+
}
109+
110+
// Read refreshes the Terraform state with the latest data.
111+
func (d *teamDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
112+
defer un(trace(ctx, "Read team data source"))
113+
114+
var data teamDataSourceModel
115+
116+
// Read Terraform configuration data into model.
117+
diags := req.Config.Get(ctx, &data)
118+
resp.Diagnostics.Append(diags...)
119+
if resp.Diagnostics.HasError() {
120+
return
121+
}
122+
123+
tflog.Info(ctx, "Get team by name", map[string]any{
124+
"name": data.Name.ValueString(),
125+
"organization_id": data.OrganizationID.ValueInt64(),
126+
})
127+
128+
// Use Forgejo client to get team by name.
129+
team, err := getOrgTeamByName(ctx, d.client, data.OrganizationID, data.Name)
130+
if err != nil {
131+
resp.Diagnostics.AddError("Unable to get team by name", err.Error())
132+
return
133+
}
134+
135+
if team == nil {
136+
err = fmt.Errorf(
137+
"no Team with name '%s' was found",
138+
data.Name.String(),
139+
)
140+
resp.Diagnostics.AddError("Unable to get team by name", err.Error())
141+
return
142+
}
143+
144+
// Map response body to model.
145+
data.ID = types.Int64Value(team.ID)
146+
data.Name = types.StringValue(team.Name)
147+
data.Description = types.StringValue(team.Description)
148+
data.OrganizationID = types.Int64Value(team.Organization.ID)
149+
data.Permission = types.StringValue(string(team.Permission))
150+
data.CanCreateOrgRepo = types.BoolValue(team.CanCreateOrgRepo)
151+
data.IncludesAllRepositories = types.BoolValue(team.IncludesAllRepositories)
152+
data.Units, diags = types.SetValueFrom(ctx, types.StringType, team.Units)
153+
154+
resp.Diagnostics.Append(diags...)
155+
156+
// Save data into Terraform state.
157+
diags = resp.State.Set(ctx, &data)
158+
resp.Diagnostics.Append(diags...)
159+
}
160+
161+
// NewTeamDataSource is a helper function to simplify the provider implementation.
162+
func NewTeamDataSource() datasource.DataSource {
163+
return &teamDataSource{}
164+
}
165+
166+
// Use Forgejo client to get team by name.
167+
func getOrgTeamByName(ctx context.Context, client *forgejo.Client, orgID types.Int64, teamName types.String) (team *forgejo.Team, err error) {
168+
tflog.Info(ctx, "Getting team in org", map[string]any{
169+
"team": teamName,
170+
"organization_id": orgID,
171+
})
172+
173+
organization, err := getOrganizationByID(ctx, client, orgID)
174+
if err != nil {
175+
return nil, err
176+
}
177+
178+
if organization == nil {
179+
err = fmt.Errorf(
180+
"no Organization with id '%d' was found",
181+
orgID.ValueInt64(),
182+
)
183+
return nil, err
184+
}
185+
186+
teams, resp, err := client.ListOrgTeams(organization.UserName, forgejo.ListTeamsOptions{})
187+
if err != nil {
188+
tflog.Error(ctx, "Error", map[string]any{
189+
"status": resp.Status,
190+
})
191+
192+
switch resp.StatusCode {
193+
case 404:
194+
err = fmt.Errorf(
195+
"the Organization with name '%s' was not found: %s",
196+
organization.UserName,
197+
err,
198+
)
199+
default:
200+
err = fmt.Errorf("unknown error: %s", err)
201+
}
202+
return nil, err
203+
}
204+
205+
for _, potentialTeam := range teams {
206+
if teamName.Equal(types.StringValue(potentialTeam.Name)) {
207+
team = potentialTeam
208+
break
209+
}
210+
}
211+
212+
return team, nil
213+
}

0 commit comments

Comments
 (0)