gist delete prompt with gist description

This commit is contained in:
danochoa 2025-01-08 17:56:02 -06:00
parent 7d1d261c82
commit 9c65a32ef8
6 changed files with 78 additions and 49 deletions

View file

@ -76,15 +76,6 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
}
func deleteRun(opts *DeleteOptions) error {
gistID := opts.Selector
if strings.Contains(gistID, "/") {
id, err := shared.GistIDFromURL(gistID)
if err != nil {
return err
}
gistID = id
}
client, err := opts.HttpClient()
if err != nil {
return err
@ -97,16 +88,28 @@ func deleteRun(opts *DeleteOptions) error {
host, _ := cfg.Authentication().DefaultHost()
cs := opts.IO.ColorScheme()
if gistID == "" {
gistID, err = shared.PromptGists(opts.Prompter, client, host, cs)
gistID := opts.Selector
if strings.Contains(gistID, "/") {
id, err := shared.GistIDFromURL(gistID)
if err != nil {
return err
}
gistID = id
}
cs := opts.IO.ColorScheme()
var gist *shared.Gist
if gistID == "" {
gist, err = shared.PromptGists(opts.Prompter, client, host, cs)
} else {
gist, err = shared.GetGist(client, host, gistID)
}
if err != nil {
return err
}
if !opts.Confirmed {
confirmed, err := opts.Prompter.Confirm(fmt.Sprintf("Delete \"%s\" gist?", gistID), false)
confirmed, err := opts.Prompter.Confirm(fmt.Sprintf("Delete \"%s\" gist?", gist.Description), false)
if err != nil {
return err
}
@ -116,9 +119,9 @@ func deleteRun(opts *DeleteOptions) error {
}
apiClient := api.NewClientFromHTTP(client)
if err := deleteGist(apiClient, host, gistID, opts); err != nil {
if err := deleteGist(apiClient, host, gist, opts); err != nil {
if errors.Is(err, shared.NotFoundErr) {
return fmt.Errorf("unable to delete gist %s: either the gist is not found or it is not owned by you", gistID)
return fmt.Errorf("unable to delete gist %s: either the gist is not found or it is not owned by you", gist.Description)
}
return err
}
@ -126,8 +129,8 @@ func deleteRun(opts *DeleteOptions) error {
return nil
}
func deleteGist(apiClient *api.Client, hostname string, gistID string, opts *DeleteOptions) error {
path := "gists/" + gistID
func deleteGist(apiClient *api.Client, hostname string, gist *shared.Gist, opts *DeleteOptions) error {
path := "gists/" + gist.ID
err := apiClient.REST(hostname, "DELETE", path, nil, nil)
if err != nil {
var httpErr api.HTTPError
@ -141,7 +144,7 @@ func deleteGist(apiClient *api.Client, hostname string, gistID string, opts *Del
fmt.Fprintf(opts.IO.Out,
"%s Gist \"%s\" deleted\n",
cs.SuccessIcon(),
gistID)
gist.Description)
}
return nil
}

View file

@ -10,6 +10,7 @@ import (
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/internal/gh"
"github.com/cli/cli/v2/internal/prompter"
"github.com/cli/cli/v2/pkg/cmd/gist/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams"
@ -131,6 +132,8 @@ func Test_deleteRun(t *testing.T) {
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Description: "1234"}))
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(200, "{}"))
},
@ -159,6 +162,8 @@ func Test_deleteRun(t *testing.T) {
Confirmed: true,
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Description: "1234"}))
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(200, "{}"))
},
@ -186,6 +191,24 @@ func Test_deleteRun(t *testing.T) {
opts: &DeleteOptions{
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Description: "1234"}))
},
cancel: true,
wantErr: true,
wantStdout: "",
wantStderr: "",
},
{
name: "cancel delete with url",
opts: &DeleteOptions{
Selector: "https://gist.github.com/myrepo/1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Description: "1234"}))
},
cancel: true,
wantErr: true,
wantStdout: "",
@ -207,6 +230,8 @@ func Test_deleteRun(t *testing.T) {
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Description: "1234"}))
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(404, "{}"))
},
@ -237,7 +262,7 @@ func Test_deleteRun(t *testing.T) {
{
"name": "1234",
"files": [{ "name": "cool.txt" }],
"description": "",
"description": "1234",
"updatedAt": "%s",
"isPublic": true
}
@ -246,7 +271,7 @@ func Test_deleteRun(t *testing.T) {
)),
)
pm.RegisterSelect("Select a gist", []string{"cool.txt about 6 hours ago"}, func(_, _ string, opts []string) (int, error) {
pm.RegisterSelect("Select a gist", []string{"cool.txt 1234 about 6 hours ago"}, func(_, _ string, opts []string) (int, error) {
return 0, nil
})
}

View file

@ -108,15 +108,16 @@ func editRun(opts *EditOptions) error {
if gistID == "" {
cs := opts.IO.ColorScheme()
if gistID == "" {
gistID, err = shared.PromptGists(opts.Prompter, client, host, cs)
gist, err := shared.PromptGists(opts.Prompter, client, host, cs)
if err != nil {
return err
}
if gistID == "" {
if gist.ID == "" {
fmt.Fprintln(opts.IO.Out, "No gists found.")
return nil
}
gistID = gist.ID
}
}

View file

@ -202,21 +202,20 @@ func IsBinaryContents(contents []byte) bool {
return isBinary
}
func PromptGists(prompter prompter.Prompter, client *http.Client, host string, cs *iostreams.ColorScheme) (gistID string, err error) {
func PromptGists(prompter prompter.Prompter, client *http.Client, host string, cs *iostreams.ColorScheme) (gist *Gist, err error) {
gists, err := ListGists(client, host, 10, nil, false, "all")
if err != nil {
return "", err
return &Gist{}, err
}
if len(gists) == 0 {
return "", nil
return &Gist{}, nil
}
var opts []string
var gistIDs = make([]string, len(gists))
// var opts []string
var opts = make([]string, len(gists))
for i, gist := range gists {
gistIDs[i] = gist.ID
description := ""
gistName := ""
@ -234,15 +233,15 @@ func PromptGists(prompter prompter.Prompter, client *http.Client, host string, c
gistTime := text.FuzzyAgo(time.Now(), gist.UpdatedAt)
// TODO: support dynamic maxWidth
description = text.Truncate(100, text.RemoveExcessiveWhitespace(description))
opt := fmt.Sprintf("%s %s %s", cs.Bold(gistName), description, cs.Gray(gistTime))
opts = append(opts, opt)
opts[i] = fmt.Sprintf("%s %s %s", cs.Bold(gistName), description, cs.Gray(gistTime))
// opts = append(opts, opt)
}
result, err := prompter.Select("Select a gist", "", opts)
if err != nil {
return "", err
return &Gist{}, err
}
return gistIDs[result], nil
return &gists[result], nil
}

View file

@ -93,12 +93,15 @@ func TestIsBinaryContents(t *testing.T) {
}
func TestPromptGists(t *testing.T) {
sixHours, _ := time.ParseDuration("6h")
sixHoursAgo := time.Now().Add(-sixHours)
sixHoursAgoFormatted := sixHoursAgo.Format(time.RFC3339Nano)
tests := []struct {
name string
prompterStubs func(pm *prompter.MockPrompter)
response string
wantOut string
gist *Gist
wantOut Gist
wantErr bool
}{
{
@ -112,21 +115,21 @@ func TestPromptGists(t *testing.T) {
},
response: `{ "data": { "viewer": { "gists": { "nodes": [
{
"name": "gistid1",
"name": "1234",
"files": [{ "name": "cool.txt" }],
"description": "",
"updatedAt": "%[1]v",
"isPublic": true
},
{
"name": "gistid2",
"name": "5678",
"files": [{ "name": "gistfile0.txt" }],
"description": "",
"updatedAt": "%[1]v",
"isPublic": true
}
] } } } }`,
wantOut: "gistid1",
wantOut: Gist{ID: "1234", Files: map[string]*GistFile{"cool.txt": {Filename: "cool.txt"}}, UpdatedAt: sixHoursAgo, Public: true},
},
{
name: "multiple files, select second gist",
@ -139,26 +142,26 @@ func TestPromptGists(t *testing.T) {
},
response: `{ "data": { "viewer": { "gists": { "nodes": [
{
"name": "gistid1",
"name": "1234",
"files": [{ "name": "cool.txt" }],
"description": "",
"updatedAt": "%[1]v",
"isPublic": true
},
{
"name": "gistid2",
"name": "5678",
"files": [{ "name": "gistfile0.txt" }],
"description": "",
"updatedAt": "%[1]v",
"isPublic": true
}
] } } } }`,
wantOut: "gistid2",
wantOut: Gist{ID: "5678", Files: map[string]*GistFile{"gistfile0.txt": {Filename: "gistfile0.txt"}}, UpdatedAt: sixHoursAgo, Public: true},
},
{
name: "no files",
response: `{ "data": { "viewer": { "gists": { "nodes": [] } } } }`,
wantOut: "",
wantOut: Gist{},
},
}
@ -166,15 +169,12 @@ func TestPromptGists(t *testing.T) {
for _, tt := range tests {
reg := &httpmock.Registry{}
const query = `query GistList\b`
sixHours, _ := time.ParseDuration("6h")
sixHoursAgo := time.Now().Add(-sixHours)
reg.Register(
httpmock.GraphQL(query),
httpmock.StringResponse(fmt.Sprintf(
tt.response,
sixHoursAgo.Format(time.RFC3339),
sixHoursAgoFormatted,
)),
)
client := &http.Client{Transport: reg}
@ -185,9 +185,9 @@ func TestPromptGists(t *testing.T) {
tt.prompterStubs(mockPrompter)
}
gistID, err := PromptGists(mockPrompter, client, "github.com", ios.ColorScheme())
gist, err := PromptGists(mockPrompter, client, "github.com", ios.ColorScheme())
assert.NoError(t, err)
assert.Equal(t, tt.wantOut, gistID)
assert.Equal(t, tt.wantOut.ID, gist.ID)
reg.Verify(t)
})
}

View file

@ -89,15 +89,16 @@ func viewRun(opts *ViewOptions) error {
cs := opts.IO.ColorScheme()
if gistID == "" {
gistID, err = shared.PromptGists(opts.Prompter, client, hostname, cs)
gist, err := shared.PromptGists(opts.Prompter, client, hostname, cs)
if err != nil {
return err
}
if gistID == "" {
if gist.ID == "" {
fmt.Fprintln(opts.IO.Out, "No gists found.")
return nil
}
gistID = gist.ID
}
if opts.Web {