Skip to content

Commit c20bfef

Browse files
committed
refactor: migrate resource_github_issue_label to context-aware CRUD and tflog
1 parent 9870fc6 commit c20bfef

1 file changed

Lines changed: 145 additions & 107 deletions

File tree

github/resource_github_issue_label.go

Lines changed: 145 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@ package github
33
import (
44
"context"
55
"errors"
6-
"log"
76
"net/http"
87

98
"github.com/google/go-github/v88/github"
9+
"github.com/hashicorp/terraform-plugin-log/tflog"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1011
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1112
)
1213

1314
func resourceGithubIssueLabel() *schema.Resource {
1415
return &schema.Resource{
15-
Create: resourceGithubIssueLabelCreateOrUpdate,
16-
Read: resourceGithubIssueLabelRead,
17-
Update: resourceGithubIssueLabelCreateOrUpdate,
18-
Delete: resourceGithubIssueLabelDelete,
16+
CreateContext: resourceGithubIssueLabelCreate,
17+
ReadContext: resourceGithubIssueLabelRead,
18+
UpdateContext: resourceGithubIssueLabelUpdate,
19+
DeleteContext: resourceGithubIssueLabelDelete,
1920
Importer: &schema.ResourceImporter{
20-
StateContext: schema.ImportStatePassthroughContext,
21+
StateContext: resourceGithubIssueLabelImport,
2122
},
2223

2324
Schema: map[string]*schema.Schema{
@@ -60,150 +61,187 @@ func resourceGithubIssueLabel() *schema.Resource {
6061
}
6162
}
6263

63-
// resourceGithubIssueLabelCreateOrUpdate idempotently creates or updates an
64-
// issue label. Issue labels are keyed off of their "name", so pre-existing
65-
// issue labels result in a 422 HTTP error if they exist outside of Terraform.
66-
// Normally this would not be an issue, except new repositories are created with
67-
// a "default" set of labels, and those labels easily conflict with custom ones.
68-
//
69-
// This function will first check if the label exists, and then issue an update,
70-
// otherwise it will create. This is also advantageous in that we get to use the
71-
// same function for two schema funcs.
72-
73-
func resourceGithubIssueLabelCreateOrUpdate(d *schema.ResourceData, meta any) error {
74-
client := meta.(*Owner).v3client
75-
orgName := meta.(*Owner).name
76-
repoName := d.Get("repository").(string)
77-
name := d.Get("name").(string)
78-
color := d.Get("color").(string)
64+
// resourceGithubIssueLabelCreate creates an issue label.
65+
func resourceGithubIssueLabelCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
66+
meta, _ := m.(*Owner)
67+
client := meta.v3client
68+
orgName := meta.name
69+
repoName, ok := d.Get("repository").(string)
70+
if !ok {
71+
return diag.Errorf(`expected "repository" to be string`)
72+
}
73+
name, ok := d.Get("name").(string)
74+
if !ok {
75+
return diag.Errorf(`expected "name" to be string`)
76+
}
77+
color, ok := d.Get("color").(string)
78+
if !ok {
79+
return diag.Errorf(`expected "color" to be string`)
80+
}
7981

8082
label := &github.Label{
8183
Name: new(name),
8284
Color: new(color),
8385
}
84-
ctx := context.Background()
85-
if !d.IsNewResource() {
86-
ctx = context.WithValue(ctx, ctxId, d.Id())
87-
}
88-
89-
// Pull out the original name. If we already have a resource, this is the
90-
// parsed ID. If not, it's the value given to the resource.
91-
var originalName string
92-
if d.Id() == "" {
93-
originalName = name
94-
} else {
95-
var err error
96-
_, originalName, err = parseID2(d.Id())
97-
if err != nil {
98-
return err
86+
87+
if v, ok := d.GetOk("description"); ok {
88+
description, ok := v.(string)
89+
if !ok {
90+
return diag.Errorf(`expected "description" to be string`)
9991
}
92+
label.Description = &description
10093
}
10194

102-
existing, resp, err := client.Issues.GetLabel(ctx,
103-
orgName, repoName, originalName)
104-
if err != nil && resp.StatusCode != http.StatusNotFound {
105-
return err
95+
githubLabel, resp, err := client.Issues.CreateLabel(ctx, orgName, repoName, label)
96+
if err != nil {
97+
return diag.FromErr(err)
10698
}
107-
108-
if existing != nil {
109-
label.Description = new(d.Get("description").(string))
110-
111-
// Pull out the original name. If we already have a resource, this is the
112-
// parsed ID. If not, it's the value given to the resource.
113-
var originalName string
114-
if d.Id() == "" {
115-
originalName = name
116-
} else {
117-
var err error
118-
_, originalName, err = parseID2(d.Id())
119-
if err != nil {
120-
return err
121-
}
122-
}
123-
124-
_, _, err := client.Issues.EditLabel(ctx,
125-
orgName, repoName, originalName, label)
126-
if err != nil {
127-
return err
128-
}
129-
} else {
130-
if v, ok := d.GetOk("description"); ok {
131-
label.Description = new(v.(string))
132-
}
133-
134-
_, _, err := client.Issues.CreateLabel(ctx,
135-
orgName, repoName, label)
136-
if err != nil {
137-
return err
138-
}
99+
id, err := buildID(repoName, name)
100+
if err != nil {
101+
return diag.FromErr(err)
102+
}
103+
d.SetId(id)
104+
if err := d.Set("url", githubLabel.GetURL()); err != nil {
105+
return diag.FromErr(err)
139106
}
140107

141-
d.SetId(buildTwoPartID(repoName, name))
142-
143-
return resourceGithubIssueLabelRead(d, meta)
108+
if err := d.Set("etag", resp.Header.Get("ETag")); err != nil {
109+
return diag.FromErr(err)
110+
}
111+
return nil
144112
}
145113

146-
func resourceGithubIssueLabelRead(d *schema.ResourceData, meta any) error {
147-
client := meta.(*Owner).v3client
148-
repoName, name, err := parseID2(d.Id())
149-
if err != nil {
150-
return err
114+
func resourceGithubIssueLabelRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
115+
meta, _ := m.(*Owner)
116+
client := meta.v3client
117+
repoName, ok := d.Get("repository").(string)
118+
if !ok {
119+
return diag.Errorf(`expected "repository" to be string`)
120+
}
121+
name, ok := d.Get("name").(string)
122+
if !ok {
123+
return diag.Errorf(`expected "name" to be string`)
151124
}
152125

153-
orgName := meta.(*Owner).name
154-
ctx := context.WithValue(context.Background(), ctxId, d.Id())
126+
orgName := meta.name
155127
if !d.IsNewResource() {
156128
ctx = context.WithValue(ctx, ctxEtag, d.Get("etag").(string))
157129
}
158130

159-
githubLabel, resp, err := client.Issues.GetLabel(ctx,
160-
orgName, repoName, name)
131+
githubLabel, resp, err := client.Issues.GetLabel(ctx, orgName, repoName, name)
161132
if err != nil {
162133
var ghErr *github.ErrorResponse
163134
if errors.As(err, &ghErr) {
164135
if ghErr.Response.StatusCode == http.StatusNotModified {
165136
return nil
166137
}
167138
if ghErr.Response.StatusCode == http.StatusNotFound {
168-
log.Printf("[INFO] Removing label %s (%s/%s) from state because it no longer exists in GitHub",
169-
name, orgName, repoName)
139+
tflog.Info(ctx, "Removing label from state because it no longer exists in GitHub", map[string]any{"name": name, "org_name": orgName, "repo_name": repoName})
170140
d.SetId("")
171141
return nil
172142
}
173143
}
174-
return err
144+
return diag.FromErr(err)
175145
}
176146

177147
if err = d.Set("etag", resp.Header.Get("ETag")); err != nil {
178-
return err
148+
return diag.FromErr(err)
179149
}
180-
if err = d.Set("repository", repoName); err != nil {
181-
return err
150+
if err = d.Set("url", githubLabel.GetURL()); err != nil {
151+
return diag.FromErr(err)
182152
}
183-
if err = d.Set("name", name); err != nil {
184-
return err
153+
154+
return nil
155+
}
156+
157+
func resourceGithubIssueLabelUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
158+
meta, _ := m.(*Owner)
159+
client := meta.v3client
160+
orgName := meta.name
161+
repoName, ok := d.Get("repository").(string)
162+
if !ok {
163+
return diag.Errorf(`expected "repository" to be string`)
185164
}
186-
if err = d.Set("color", githubLabel.GetColor()); err != nil {
187-
return err
165+
name, ok := d.Get("name").(string)
166+
if !ok {
167+
return diag.Errorf(`expected "name" to be string`)
188168
}
189-
if err = d.Set("description", githubLabel.GetDescription()); err != nil {
190-
return err
169+
color, ok := d.Get("color").(string)
170+
if !ok {
171+
return diag.Errorf(`expected "color" to be string`)
191172
}
192-
if err = d.Set("url", githubLabel.GetURL()); err != nil {
193-
return err
173+
174+
originalName := name
175+
if d.HasChange("name") {
176+
oldName, _ := d.GetChange("name")
177+
oldNameString, ok := oldName.(string)
178+
if !ok {
179+
return diag.Errorf(`expected old "name" to be string`)
180+
}
181+
originalName = oldNameString
182+
}
183+
label := &github.Label{
184+
Name: new(name),
185+
Color: new(color),
186+
}
187+
if v, ok := d.GetOk("description"); ok {
188+
description, ok := v.(string)
189+
if !ok {
190+
return diag.Errorf(`expected "description" to be string`)
191+
}
192+
label.Description = &description
193+
}
194+
githubLabel, resp, err := client.Issues.EditLabel(ctx, orgName, repoName, originalName, label)
195+
if err != nil {
196+
return diag.FromErr(err)
197+
}
198+
id, err := buildID(repoName, name)
199+
if err != nil {
200+
return diag.FromErr(err)
201+
}
202+
d.SetId(id)
203+
204+
if err := d.Set("url", githubLabel.GetURL()); err != nil {
205+
return diag.FromErr(err)
206+
}
207+
208+
if err := d.Set("etag", resp.Header.Get("ETag")); err != nil {
209+
return diag.FromErr(err)
194210
}
195211

196212
return nil
197213
}
198214

199-
func resourceGithubIssueLabelDelete(d *schema.ResourceData, meta any) error {
200-
client := meta.(*Owner).v3client
201-
202-
orgName := meta.(*Owner).name
203-
repoName := d.Get("repository").(string)
204-
name := d.Get("name").(string)
205-
ctx := context.WithValue(context.Background(), ctxId, d.Id())
215+
func resourceGithubIssueLabelDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
216+
meta, _ := m.(*Owner)
217+
client := meta.v3client
218+
orgName := meta.name
206219

220+
repoName, ok := d.Get("repository").(string)
221+
if !ok {
222+
return diag.Errorf(`expected "repository" to be string`)
223+
}
224+
name, ok := d.Get("name").(string)
225+
if !ok {
226+
return diag.Errorf(`expected "name" to be string`)
227+
}
207228
_, err := client.Issues.DeleteLabel(ctx, orgName, repoName, name)
208-
return handleArchivedRepoDelete(err, "issue label", name, orgName, repoName)
229+
return diag.FromErr(handleArchivedRepoDelete(err, "issue label", name, orgName, repoName))
230+
}
231+
232+
func resourceGithubIssueLabelImport(_ context.Context, d *schema.ResourceData, _ any) ([]*schema.ResourceData, error) {
233+
repoName, name, err := parseID2(d.Id())
234+
if err != nil {
235+
return nil, err
236+
}
237+
238+
if err := d.Set("repository", repoName); err != nil {
239+
return nil, err
240+
}
241+
242+
if err := d.Set("name", name); err != nil {
243+
return nil, err
244+
}
245+
246+
return []*schema.ResourceData{d}, nil
209247
}

0 commit comments

Comments
 (0)