Skip to content

Commit e17d203

Browse files
Merge pull request cli#12838 from cli/remove-state-reason-duplicate-detection
Remove unnecessary StateReason and StateReasonDuplicate feature detection
2 parents ff8873d + 4f2304d commit e17d203

6 files changed

Lines changed: 12 additions & 260 deletions

File tree

internal/featuredetection/feature_detection.go

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,11 @@ type Detector interface {
2323
}
2424

2525
type IssueFeatures struct {
26-
StateReason bool
27-
StateReasonDuplicate bool
28-
ActorIsAssignable bool
26+
ActorIsAssignable bool
2927
}
3028

3129
var allIssueFeatures = IssueFeatures{
32-
StateReason: true,
33-
StateReasonDuplicate: true,
34-
ActorIsAssignable: true,
30+
ActorIsAssignable: true,
3531
}
3632

3733
type PullRequestFeatures struct {
@@ -139,47 +135,9 @@ func (d *detector) IssueFeatures() (IssueFeatures, error) {
139135
return allIssueFeatures, nil
140136
}
141137

142-
features := IssueFeatures{
143-
StateReason: false,
144-
StateReasonDuplicate: false,
145-
ActorIsAssignable: false, // replaceActorsForAssignable GraphQL mutation unavailable on GHES
146-
}
147-
148-
var featureDetection struct {
149-
Issue struct {
150-
Fields []struct {
151-
Name string
152-
} `graphql:"fields(includeDeprecated: true)"`
153-
} `graphql:"Issue: __type(name: \"Issue\")"`
154-
IssueClosedStateReason struct {
155-
EnumValues []struct {
156-
Name string
157-
} `graphql:"enumValues(includeDeprecated: true)"`
158-
} `graphql:"IssueClosedStateReason: __type(name: \"IssueClosedStateReason\")"`
159-
}
160-
161-
gql := api.NewClientFromHTTP(d.httpClient)
162-
err := gql.Query(d.host, "Issue_fields", &featureDetection, nil)
163-
if err != nil {
164-
return features, err
165-
}
166-
167-
for _, field := range featureDetection.Issue.Fields {
168-
if field.Name == "stateReason" {
169-
features.StateReason = true
170-
}
171-
}
172-
173-
if features.StateReason {
174-
for _, enumValue := range featureDetection.IssueClosedStateReason.EnumValues {
175-
if enumValue.Name == "DUPLICATE" {
176-
features.StateReasonDuplicate = true
177-
break
178-
}
179-
}
180-
}
181-
182-
return features, nil
138+
return IssueFeatures{
139+
ActorIsAssignable: false, // replaceActorsForAssignable GraphQL mutation unavailable on GHES
140+
}, nil
183141
}
184142

185143
func (d *detector) PullRequestFeatures() (PullRequestFeatures, error) {

internal/featuredetection/feature_detection_test.go

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,73 +23,23 @@ func TestIssueFeatures(t *testing.T) {
2323
name: "github.com",
2424
hostname: "github.com",
2525
wantFeatures: IssueFeatures{
26-
StateReason: true,
27-
StateReasonDuplicate: true,
28-
ActorIsAssignable: true,
26+
ActorIsAssignable: true,
2927
},
3028
wantErr: false,
3129
},
3230
{
3331
name: "ghec data residency (ghe.com)",
3432
hostname: "stampname.ghe.com",
3533
wantFeatures: IssueFeatures{
36-
StateReason: true,
37-
StateReasonDuplicate: true,
38-
ActorIsAssignable: true,
34+
ActorIsAssignable: true,
3935
},
4036
wantErr: false,
4137
},
4238
{
43-
name: "GHE empty response",
44-
hostname: "git.my.org",
45-
queryResponse: map[string]string{
46-
`query Issue_fields\b`: `{"data": {}}`,
47-
},
48-
wantFeatures: IssueFeatures{
49-
StateReason: false,
50-
StateReasonDuplicate: false,
51-
ActorIsAssignable: false,
52-
},
53-
wantErr: false,
54-
},
55-
{
56-
name: "GHE has state reason field without duplicate enum",
39+
name: "GHE",
5740
hostname: "git.my.org",
58-
queryResponse: map[string]string{
59-
`query Issue_fields\b`: heredoc.Doc(`
60-
{ "data": { "Issue": { "fields": [
61-
{"name": "stateReason"}
62-
] }, "IssueClosedStateReason": { "enumValues": [
63-
{"name": "COMPLETED"},
64-
{"name": "NOT_PLANNED"}
65-
] } } }
66-
`),
67-
},
68-
wantFeatures: IssueFeatures{
69-
StateReason: true,
70-
StateReasonDuplicate: false,
71-
ActorIsAssignable: false,
72-
},
73-
wantErr: false,
74-
},
75-
{
76-
name: "GHE has duplicate state reason enum value",
77-
hostname: "git.my.org",
78-
queryResponse: map[string]string{
79-
`query Issue_fields\b`: heredoc.Doc(`
80-
{ "data": { "Issue": { "fields": [
81-
{"name": "stateReason"}
82-
] }, "IssueClosedStateReason": { "enumValues": [
83-
{"name": "COMPLETED"},
84-
{"name": "NOT_PLANNED"},
85-
{"name": "DUPLICATE"}
86-
] } } }
87-
`),
88-
},
8941
wantFeatures: IssueFeatures{
90-
StateReason: true,
91-
StateReasonDuplicate: true,
92-
ActorIsAssignable: false,
42+
ActorIsAssignable: false,
9343
},
9444
wantErr: false,
9545
},

pkg/cmd/issue/close/close.go

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ package close
33
import (
44
"fmt"
55
"net/http"
6-
"time"
76

87
"github.com/MakeNowJust/heredoc"
98
"github.com/cli/cli/v2/api"
10-
fd "github.com/cli/cli/v2/internal/featuredetection"
119
"github.com/cli/cli/v2/internal/ghrepo"
1210
"github.com/cli/cli/v2/pkg/cmd/issue/shared"
1311
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
@@ -26,8 +24,6 @@ type CloseOptions struct {
2624
Comment string
2725
Reason string
2826
DuplicateOf string
29-
30-
Detector fd.Detector
3127
}
3228

3329
func NewCmdClose(f *cmdutil.Factory, runF func(*CloseOptions) error) *cobra.Command {
@@ -165,7 +161,7 @@ func closeRun(opts *CloseOptions) error {
165161
}
166162
}
167163

168-
err = apiClose(httpClient, baseRepo, issue, opts.Detector, closeReason, duplicateIssueID)
164+
err = apiClose(httpClient, baseRepo, issue, closeReason, duplicateIssueID)
169165
if err != nil {
170166
return err
171167
}
@@ -175,36 +171,11 @@ func closeRun(opts *CloseOptions) error {
175171
return nil
176172
}
177173

178-
func apiClose(httpClient *http.Client, repo ghrepo.Interface, issue *api.Issue, detector fd.Detector, reason string, duplicateIssueID string) error {
174+
func apiClose(httpClient *http.Client, repo ghrepo.Interface, issue *api.Issue, reason string, duplicateIssueID string) error {
179175
if issue.IsPullRequest() {
180176
return api.PullRequestClose(httpClient, repo, issue.ID)
181177
}
182178

183-
if reason != "" || duplicateIssueID != "" {
184-
if detector == nil {
185-
cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
186-
detector = fd.NewDetector(cachedClient, repo.RepoHost())
187-
}
188-
features, err := detector.IssueFeatures()
189-
if err != nil {
190-
return err
191-
}
192-
// TODO stateReasonCleanup
193-
if !features.StateReason {
194-
// If StateReason is not supported silently close issue without setting StateReason.
195-
if duplicateIssueID != "" {
196-
return fmt.Errorf("closing as duplicate is not supported on %s", repo.RepoHost())
197-
}
198-
reason = ""
199-
} else if reason == "duplicate" && !features.StateReasonDuplicate {
200-
if duplicateIssueID != "" {
201-
return fmt.Errorf("closing as duplicate is not supported on %s", repo.RepoHost())
202-
}
203-
// If DUPLICATE is not supported silently close issue without setting StateReason.
204-
reason = ""
205-
}
206-
}
207-
208179
switch reason {
209180
case "":
210181
// If no reason is specified do not set it.

pkg/cmd/issue/close/close_test.go

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"net/http"
66
"testing"
77

8-
fd "github.com/cli/cli/v2/internal/featuredetection"
98
"github.com/cli/cli/v2/internal/ghrepo"
109
"github.com/cli/cli/v2/pkg/cmd/issue/argparsetest"
1110
"github.com/cli/cli/v2/pkg/cmdutil"
@@ -16,15 +15,6 @@ import (
1615
"github.com/stretchr/testify/require"
1716
)
1817

19-
type issueFeaturesDetectorMock struct {
20-
fd.EnabledDetectorMock
21-
issueFeatures fd.IssueFeatures
22-
}
23-
24-
func (md *issueFeaturesDetectorMock) IssueFeatures() (fd.IssueFeatures, error) {
25-
return md.issueFeatures, nil
26-
}
27-
2818
func TestNewCmdClose(t *testing.T) {
2919
// Test shared parsing of issue number / URL.
3020
argparsetest.TestArgParsing(t, NewCmdClose)
@@ -194,7 +184,6 @@ func TestCloseRun(t *testing.T) {
194184
opts: &CloseOptions{
195185
IssueNumber: 13,
196186
Reason: "not planned",
197-
Detector: &fd.EnabledDetectorMock{},
198187
},
199188
httpStubs: func(reg *httpmock.Registry) {
200189
reg.Register(
@@ -222,7 +211,6 @@ func TestCloseRun(t *testing.T) {
222211
opts: &CloseOptions{
223212
IssueNumber: 13,
224213
Reason: "duplicate",
225-
Detector: &fd.EnabledDetectorMock{},
226214
},
227215
httpStubs: func(reg *httpmock.Registry) {
228216
reg.Register(
@@ -250,7 +238,6 @@ func TestCloseRun(t *testing.T) {
250238
opts: &CloseOptions{
251239
IssueNumber: 13,
252240
DuplicateOf: "99",
253-
Detector: &fd.EnabledDetectorMock{},
254241
},
255242
httpStubs: func(reg *httpmock.Registry) {
256243
reg.Register(
@@ -282,71 +269,6 @@ func TestCloseRun(t *testing.T) {
282269
},
283270
wantStderr: "✓ Closed issue OWNER/REPO#13 (The title of the issue)\n",
284271
},
285-
{
286-
name: "close issue with duplicate reason when duplicate is not supported",
287-
opts: &CloseOptions{
288-
IssueNumber: 13,
289-
Reason: "duplicate",
290-
Detector: &issueFeaturesDetectorMock{
291-
issueFeatures: fd.IssueFeatures{
292-
StateReason: true,
293-
StateReasonDuplicate: false,
294-
},
295-
},
296-
},
297-
httpStubs: func(reg *httpmock.Registry) {
298-
reg.Register(
299-
httpmock.GraphQL(`query IssueByNumber\b`),
300-
httpmock.StringResponse(`
301-
{ "data": { "repository": {
302-
"hasIssuesEnabled": true,
303-
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
304-
} } }`),
305-
)
306-
reg.Register(
307-
httpmock.GraphQL(`mutation IssueClose\b`),
308-
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
309-
func(inputs map[string]interface{}) {
310-
assert.Equal(t, 1, len(inputs))
311-
assert.Equal(t, "THE-ID", inputs["issueId"])
312-
}),
313-
)
314-
},
315-
wantStderr: "✓ Closed issue OWNER/REPO#13 (The title of the issue)\n",
316-
},
317-
{
318-
name: "close issue as duplicate when duplicate is not supported",
319-
opts: &CloseOptions{
320-
IssueNumber: 13,
321-
DuplicateOf: "99",
322-
Detector: &issueFeaturesDetectorMock{
323-
issueFeatures: fd.IssueFeatures{
324-
StateReason: true,
325-
StateReasonDuplicate: false,
326-
},
327-
},
328-
},
329-
httpStubs: func(reg *httpmock.Registry) {
330-
reg.Register(
331-
httpmock.GraphQL(`query IssueByNumber\b`),
332-
httpmock.StringResponse(`
333-
{ "data": { "repository": {
334-
"hasIssuesEnabled": true,
335-
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
336-
} } }`),
337-
)
338-
reg.Register(
339-
httpmock.GraphQL(`query IssueByNumber\b`),
340-
httpmock.StringResponse(`
341-
{ "data": { "repository": {
342-
"hasIssuesEnabled": true,
343-
"issue": { "id": "DUPLICATE-ID", "number": 99}
344-
} } }`),
345-
)
346-
},
347-
wantErr: true,
348-
errMsg: "closing as duplicate is not supported on github.com",
349-
},
350272
{
351273
name: "duplicate of cannot point to same issue",
352274
opts: &CloseOptions{
@@ -412,33 +334,6 @@ func TestCloseRun(t *testing.T) {
412334
wantErr: true,
413335
errMsg: "invalid value for `--duplicate-of`: invalid issue format: \"not-an-issue\"",
414336
},
415-
{
416-
name: "close issue with reason when reason is not supported",
417-
opts: &CloseOptions{
418-
IssueNumber: 13,
419-
Reason: "not planned",
420-
Detector: &fd.DisabledDetectorMock{},
421-
},
422-
httpStubs: func(reg *httpmock.Registry) {
423-
reg.Register(
424-
httpmock.GraphQL(`query IssueByNumber\b`),
425-
httpmock.StringResponse(`
426-
{ "data": { "repository": {
427-
"hasIssuesEnabled": true,
428-
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
429-
} } }`),
430-
)
431-
reg.Register(
432-
httpmock.GraphQL(`mutation IssueClose\b`),
433-
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
434-
func(inputs map[string]interface{}) {
435-
assert.Equal(t, 1, len(inputs))
436-
assert.Equal(t, "THE-ID", inputs["issueId"])
437-
}),
438-
)
439-
},
440-
wantStderr: "✓ Closed issue OWNER/REPO#13 (The title of the issue)\n",
441-
},
442337
{
443338
name: "issue already closed",
444339
opts: &CloseOptions{

pkg/cmd/issue/list/list.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,7 @@ func listRun(opts *ListOptions) error {
147147
cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
148148
opts.Detector = fd.NewDetector(cachedClient, baseRepo.RepoHost())
149149
}
150-
features, err := opts.Detector.IssueFeatures()
151-
if err != nil {
152-
return err
153-
}
154-
fields := defaultFields
155-
// TODO stateReasonCleanup
156-
if features.StateReason {
157-
fields = append(defaultFields, "stateReason")
158-
}
150+
fields := append(defaultFields, "stateReason")
159151

160152
filterOptions := prShared.FilterOptions{
161153
Entity: "issue",

0 commit comments

Comments
 (0)