X Tutup
Skip to content

Commit d300526

Browse files
vilmibmvilmibm
authored andcommitted
preserve and restore issue/pr input on failure
1 parent e92cd43 commit d300526

File tree

10 files changed

+457
-71
lines changed

10 files changed

+457
-71
lines changed

pkg/cmd/issue/create/create.go

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type CreateOptions struct {
2626

2727
RepoOverride string
2828
WebMode bool
29+
JSONFill bool
30+
JSONInput string
2931

3032
Title string
3133
Body string
@@ -62,13 +64,22 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
6264
titleProvided := cmd.Flags().Changed("title")
6365
bodyProvided := cmd.Flags().Changed("body")
6466
opts.RepoOverride, _ = cmd.Flags().GetString("repo")
67+
opts.JSONFill = cmd.Flags().Changed("json")
6568

6669
opts.Interactive = !(titleProvided && bodyProvided)
6770

6871
if opts.Interactive && !opts.IO.CanPrompt() {
6972
return &cmdutil.FlagError{Err: errors.New("must provide --title and --body when not running interactively")}
7073
}
7174

75+
if opts.JSONFill {
76+
opts.Interactive = false
77+
78+
if opts.WebMode {
79+
return errors.New("--web and --json are mutually exclusive")
80+
}
81+
}
82+
7283
if runF != nil {
7384
return runF(opts)
7485
}
@@ -83,20 +94,21 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
8394
cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Add labels by `name`")
8495
cmd.Flags().StringSliceVarP(&opts.Projects, "project", "p", nil, "Add the issue to projects by `name`")
8596
cmd.Flags().StringVarP(&opts.Milestone, "milestone", "m", "", "Add the issue to a milestone by `name`")
97+
cmd.Flags().StringVarP(&opts.JSONInput, "json", "j", "", "Use JSON to populate and submit issue")
8698

8799
return cmd
88100
}
89101

90-
func createRun(opts *CreateOptions) error {
102+
func createRun(opts *CreateOptions) (err error) {
91103
httpClient, err := opts.HttpClient()
92104
if err != nil {
93-
return err
105+
return
94106
}
95107
apiClient := api.NewClientFromHTTP(httpClient)
96108

97109
baseRepo, err := opts.BaseRepo()
98110
if err != nil {
99-
return err
111+
return
100112
}
101113

102114
templateFiles, legacyTemplate := prShared.FindTemplates(opts.RootDirOverride, "ISSUE_TEMPLATE")
@@ -123,7 +135,7 @@ func createRun(opts *CreateOptions) error {
123135
if opts.Title != "" || opts.Body != "" {
124136
openURL, err = prShared.WithPrAndIssueQueryParams(openURL, tb)
125137
if err != nil {
126-
return err
138+
return
127139
}
128140
} else if len(templateFiles) > 1 {
129141
openURL += "/choose"
@@ -140,24 +152,28 @@ func createRun(opts *CreateOptions) error {
140152

141153
repo, err := api.GitHubRepo(apiClient, baseRepo)
142154
if err != nil {
143-
return err
155+
return
144156
}
145157
if !repo.HasIssuesEnabled {
146-
return fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(baseRepo))
158+
err = fmt.Errorf("the '%s' repository has disabled issues", ghrepo.FullName(baseRepo))
159+
return
147160
}
148161

149162
action := prShared.SubmitAction
150163

151164
if opts.Interactive {
152-
editorCommand, err := cmdutil.DetermineEditor(opts.Config)
165+
var editorCommand string
166+
editorCommand, err = cmdutil.DetermineEditor(opts.Config)
153167
if err != nil {
154-
return err
168+
return
155169
}
156170

171+
defer prShared.PreserveInput(opts.IO, &tb, &err)()
172+
157173
if tb.Title == "" {
158174
err = prShared.TitleSurvey(&tb)
159175
if err != nil {
160-
return err
176+
return
161177
}
162178
}
163179

@@ -166,52 +182,61 @@ func createRun(opts *CreateOptions) error {
166182

167183
templateContent, err = prShared.TemplateSurvey(templateFiles, legacyTemplate, tb)
168184
if err != nil {
169-
return err
185+
return
170186
}
171187

172188
err = prShared.BodySurvey(&tb, templateContent, editorCommand)
173189
if err != nil {
174-
return err
190+
return
175191
}
176192

177193
if tb.Body == "" {
178194
tb.Body = templateContent
179195
}
180196
}
181197

182-
action, err := prShared.ConfirmSubmission(!tb.HasMetadata(), repo.ViewerCanTriage())
198+
var action prShared.Action
199+
action, err = prShared.ConfirmSubmission(!tb.HasMetadata(), repo.ViewerCanTriage())
183200
if err != nil {
184-
return fmt.Errorf("unable to confirm: %w", err)
201+
err = fmt.Errorf("unable to confirm: %w", err)
202+
return
185203
}
186204

187205
if action == prShared.MetadataAction {
188206
err = prShared.MetadataSurvey(opts.IO, apiClient, baseRepo, &tb)
189207
if err != nil {
190-
return err
208+
return
191209
}
192210

193211
action, err = prShared.ConfirmSubmission(!tb.HasMetadata(), false)
194212
if err != nil {
195-
return err
213+
return
196214
}
197215
}
198216

199217
if action == prShared.CancelAction {
200218
fmt.Fprintln(opts.IO.ErrOut, "Discarding.")
201-
202-
return nil
219+
return
220+
}
221+
} else if opts.JSONFill {
222+
err = prShared.FillFromJSON(opts.IO, opts.JSONInput, &tb)
223+
if err != nil {
224+
return
203225
}
226+
227+
action = prShared.SubmitAction
204228
} else {
205229
if tb.Title == "" {
206-
return fmt.Errorf("title can't be blank")
230+
err = fmt.Errorf("title can't be blank")
231+
return
207232
}
208233
}
209234

210235
if action == prShared.PreviewAction {
211236
openURL := ghrepo.GenerateRepoURL(baseRepo, "issues/new")
212237
openURL, err = prShared.WithPrAndIssueQueryParams(openURL, tb)
213238
if err != nil {
214-
return err
239+
return
215240
}
216241
if isTerminal {
217242
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL))
@@ -225,18 +250,19 @@ func createRun(opts *CreateOptions) error {
225250

226251
err = prShared.AddMetadataToIssueParams(apiClient, baseRepo, params, &tb)
227252
if err != nil {
228-
return err
253+
return
229254
}
230255

231-
newIssue, err := api.IssueCreate(apiClient, repo, params)
256+
var newIssue *api.Issue
257+
newIssue, err = api.IssueCreate(apiClient, repo, params)
232258
if err != nil {
233-
return err
259+
return
234260
}
235261

236262
fmt.Fprintln(opts.IO.Out, newIssue.URL)
237263
} else {
238264
panic("Unreachable state")
239265
}
240266

241-
return nil
267+
return
242268
}

pkg/cmd/issue/create/create_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,46 @@ func TestIssueCreate(t *testing.T) {
126126
eq(t, output.String(), "https://github.com/OWNER/REPO/issues/12\n")
127127
}
128128

129+
func TestIssueCreate_JSON(t *testing.T) {
130+
http := &httpmock.Registry{}
131+
defer http.Verify(t)
132+
133+
http.StubResponse(200, bytes.NewBufferString(`
134+
{ "data": { "repository": {
135+
"id": "REPOID",
136+
"hasIssuesEnabled": true
137+
} } }
138+
`))
139+
http.StubResponse(200, bytes.NewBufferString(`
140+
{ "data": { "createIssue": { "issue": {
141+
"URL": "https://github.com/OWNER/REPO/issues/12"
142+
} } } }
143+
`))
144+
145+
output, err := runCommand(http, true, `-j'{"title":"cool", "body":"issue"}'`)
146+
if err != nil {
147+
t.Errorf("error running command `issue create`: %v", err)
148+
}
149+
150+
bodyBytes, _ := ioutil.ReadAll(http.Requests[1].Body)
151+
reqBody := struct {
152+
Variables struct {
153+
Input struct {
154+
RepositoryID string
155+
Title string
156+
Body string
157+
}
158+
}
159+
}{}
160+
_ = json.Unmarshal(bodyBytes, &reqBody)
161+
162+
eq(t, reqBody.Variables.Input.RepositoryID, "REPOID")
163+
eq(t, reqBody.Variables.Input.Title, "cool")
164+
eq(t, reqBody.Variables.Input.Body, "issue")
165+
166+
eq(t, output.String(), "https://github.com/OWNER/REPO/issues/12\n")
167+
}
168+
129169
func TestIssueCreate_nonLegacyTemplate(t *testing.T) {
130170
http := &httpmock.Registry{}
131171
defer http.Verify(t)

0 commit comments

Comments
 (0)
X Tutup