Skip to content

Commit 6763278

Browse files
committed
feat: Add support for managing a team.
1 parent 61411d8 commit 6763278

File tree

8 files changed

+910
-0
lines changed

8 files changed

+910
-0
lines changed

README.md

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

2627
Data Sources:
@@ -30,6 +31,7 @@ Data Sources:
3031
- `forgejo_organization` ([documentation](docs/data-sources/organization.md))
3132
- `forgejo_repository` ([documentation](docs/data-sources/repository.md))
3233
- `forgejo_ssh_key` ([documentation](docs/data-sources/ssh_key.md))
34+
- `forgejo_team` ([documentation](docs/data-sources/team.md))
3335
- `forgejo_user` ([documentation](docs/data-sources/user.md))
3436

3537
## 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` (String) Name 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` (String) Name 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/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
}
@@ -258,6 +259,7 @@ func (p *forgejoProvider) Resources(_ context.Context) []func() resource.Resourc
258259
NewRepositoryActionSecretResource,
259260
NewRepositoryResource,
260261
NewSSHKeyResource,
262+
NewTeamResource,
261263
NewUserResource,
262264
}
263265
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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+
Organization types.String `tfsdk:"organization"`
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": schema.StringAttribute{
59+
Description: "Name 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": data.Organization.ValueString(),
126+
})
127+
128+
// Use Forgejo client to get team by name.
129+
team, err := getOrgTeamByName(ctx, d.client, data.Organization, 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+
"Team with name '%s' not 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.Organization = types.StringValue(team.Organization.UserName)
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, orgName types.String, teamName types.String) (team *forgejo.Team, err error) {
168+
teams, resp, err := client.ListOrgTeams(orgName.ValueString(), forgejo.ListTeamsOptions{})
169+
if err != nil {
170+
tflog.Error(ctx, "Error", map[string]any{
171+
"status": resp.Status,
172+
})
173+
174+
switch resp.StatusCode {
175+
case 404:
176+
err = fmt.Errorf(
177+
"Organization with name '%s' not found: %s",
178+
orgName,
179+
err,
180+
)
181+
default:
182+
err = fmt.Errorf("Unknown error: %s", err)
183+
}
184+
return
185+
}
186+
187+
for _, potentialTeam := range teams {
188+
if teamName.Equal(types.StringValue(potentialTeam.Name)) {
189+
team = potentialTeam
190+
break
191+
}
192+
}
193+
return
194+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package provider_test
2+
3+
import (
4+
"regexp"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
8+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
9+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
10+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
11+
)
12+
13+
func TestAccTeamDataSource(t *testing.T) {
14+
resource.Test(t, resource.TestCase{
15+
PreCheck: func() { testAccPreCheck(t) },
16+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
17+
Steps: []resource.TestStep{
18+
// Read testing (non-existent org)
19+
{
20+
Config: providerConfig + `
21+
data "forgejo_team" "test" {
22+
name = "tftest"
23+
organization = "test_org"
24+
}`,
25+
ExpectError: regexp.MustCompile("Organization with name 'test_org' not found"),
26+
},
27+
// Read testing (non-existent team)
28+
{
29+
Config: providerConfig + `
30+
data "forgejo_team" "test" {
31+
name = "test_team"
32+
organization = "tftest"
33+
}`,
34+
ExpectError: regexp.MustCompile("Team with name 'test_team' not found"),
35+
},
36+
// Create and Read testing (import if exists)
37+
{
38+
Config: providerConfig + `
39+
resource "forgejo_team" "test" {
40+
name = "test_team"
41+
organization = "tftest"
42+
can_create_org_repo = true
43+
include_all_repositories = true
44+
permission = "read"
45+
units = [ "repo.code" ]
46+
}
47+
data "forgejo_team" "test" {
48+
name = "test_team"
49+
organization = "tftest"
50+
}`,
51+
ConfigStateChecks: []statecheck.StateCheck{
52+
statecheck.ExpectKnownValue("forgejo_team.test", tfjsonpath.New("name"), knownvalue.StringExact("test_team")),
53+
statecheck.ExpectKnownValue("forgejo_team.test", tfjsonpath.New("organization"), knownvalue.StringExact("tftest")),
54+
statecheck.ExpectKnownValue("forgejo_team.test", tfjsonpath.New("can_create_org_repo"), knownvalue.Bool(true)),
55+
statecheck.ExpectKnownValue("forgejo_team.test", tfjsonpath.New("include_all_repositories"), knownvalue.Bool(true)),
56+
statecheck.ExpectKnownValue("forgejo_team.test", tfjsonpath.New("permission"), knownvalue.StringExact("read")),
57+
statecheck.ExpectKnownValue("forgejo_team.test", tfjsonpath.New("units"), knownvalue.SetExact([]knownvalue.Check{
58+
knownvalue.StringExact("repo.code"),
59+
})),
60+
},
61+
},
62+
},
63+
})
64+
}

0 commit comments

Comments
 (0)