Skip to content
Merged
2 changes: 2 additions & 0 deletions pkg/cmd/repo/autolink/autolink.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/MakeNowJust/heredoc"
cmdCreate "github.com/cli/cli/v2/pkg/cmd/repo/autolink/create"
cmdList "github.com/cli/cli/v2/pkg/cmd/repo/autolink/list"
cmdView "github.com/cli/cli/v2/pkg/cmd/repo/autolink/view"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/spf13/cobra"
)
Expand All @@ -24,6 +25,7 @@ func NewCmdAutolink(f *cmdutil.Factory) *cobra.Command {

cmd.AddCommand(cmdList.NewCmdList(f, nil))
cmd.AddCommand(cmdCreate.NewCmdCreate(f, nil))
cmd.AddCommand(cmdView.NewCmdView(f, nil))

return cmd
}
4 changes: 2 additions & 2 deletions pkg/cmd/repo/autolink/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ func createRun(opts *createOptions) error {

cs := opts.IO.ColorScheme()
fmt.Fprintf(opts.IO.Out,
"%s Created repository autolink %d on %s\n",
"%s Created repository autolink %s on %s\n",
cs.SuccessIconWithColor(cs.Green),
autolink.ID,
cs.Cyanf("%d", autolink.ID),
ghrepo.FullName(repo))

return nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/repo/autolink/list/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/require"
)

func TestAutoLinkLister_List(t *testing.T) {
func TestAutolinkLister_List(t *testing.T) {
tests := []struct {
name string
repo ghrepo.Interface
Expand Down
13 changes: 4 additions & 9 deletions pkg/cmd/repo/autolink/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ import (
"github.com/spf13/cobra"
)

var autolinkFields = []string{
"id",
"isAlphanumeric",
"keyPrefix",
"urlTemplate",
}

type listOptions struct {
BaseRepo func() (ghrepo.Interface, error)
Browser browser.Browser
Expand Down Expand Up @@ -70,7 +63,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*listOptions) error) *cobra.Comman
}

cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "List autolink references in the web browser")
cmdutil.AddJSONFlags(cmd, &opts.Exporter, autolinkFields)
cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.AutolinkFields)

return cmd
}
Expand Down Expand Up @@ -111,8 +104,10 @@ func listRun(opts *listOptions) error {

tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "KEY PREFIX", "URL TEMPLATE", "ALPHANUMERIC"))

cs := opts.IO.ColorScheme()

for _, autolink := range autolinks {
tp.AddField(fmt.Sprintf("%d", autolink.ID))
tp.AddField(cs.Cyanf("%d", autolink.ID))
tp.AddField(autolink.KeyPrefix)
tp.AddField(autolink.URLTemplate)
tp.AddField(strconv.FormatBool(autolink.IsAlphanumeric))
Expand Down
7 changes: 7 additions & 0 deletions pkg/cmd/repo/autolink/shared/autolink.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ type Autolink struct {
URLTemplate string `json:"url_template"`
}

var AutolinkFields = []string{
"id",
"isAlphanumeric",
"keyPrefix",
"urlTemplate",
}

func (a *Autolink) ExportData(fields []string) map[string]interface{} {
return cmdutil.StructExportData(a, fields)
}
46 changes: 46 additions & 0 deletions pkg/cmd/repo/autolink/view/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package view

import (
"encoding/json"
"fmt"
"net/http"

"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
)

type AutolinkViewer struct {
HTTPClient *http.Client
}

func (a *AutolinkViewer) View(repo ghrepo.Interface, id string) (*shared.Autolink, error) {
path := fmt.Sprintf("repos/%s/%s/autolinks/%s", repo.RepoOwner(), repo.RepoName(), id)
url := ghinstance.RESTPrefix(repo.RepoHost()) + path
Comment on lines +19 to +20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting it took me a hot minute to see path being reused later for error messaging if 404 arises.

req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}

resp, err := a.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusNotFound {
return nil, fmt.Errorf("HTTP 404: Either no autolink with this ID exists for this repository or you are missing admin rights to the repository. (https://api.github.com/%s)", path)
} else if resp.StatusCode > 299 {
return nil, api.HandleHTTPError(resp)
}

var autolink shared.Autolink
err = json.NewDecoder(resp.Body).Decode(&autolink)

if err != nil {
return nil, err
}

return &autolink, nil
}
102 changes: 102 additions & 0 deletions pkg/cmd/repo/autolink/view/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package view

import (
"fmt"
"net/http"
"testing"

"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
"github.com/cli/cli/v2/pkg/httpmock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAutolinkViewer_View(t *testing.T) {
repo := ghrepo.New("OWNER", "REPO")

tests := []struct {
name string
id string
stubStatus int
stubRespJSON string

expectedAutolink *shared.Autolink
expectErr bool
expectedErrMsg string
}{
{
name: "200 successful alphanumeric view",
id: "123",
stubStatus: 200,
stubRespJSON: `{
"id": 123,
"key_prefix": "TICKET-",
"url_template": "https://example.com/TICKET?query=<num>",
"is_alphanumeric": true
}`,
expectedAutolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
Comment thread
andyfeller marked this conversation as resolved.
{
name: "200 successful numeric view",
id: "123",
stubStatus: 200,
stubRespJSON: `{
"id": 123,
"key_prefix": "TICKET-",
"url_template": "https://example.com/TICKET?query=<num>",
"is_alphanumeric": false
}`,
expectedAutolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: false,
},
},
{
name: "404 repo or autolink not found",
id: "123",
stubStatus: 404,
stubRespJSON: `{
"message": "Not Found",
"documentation_url": "https://docs.github.com/rest/repos/autolinks#get-an-autolink-reference-of-a-repository",
"status": "404"
}`,
expectErr: true,
expectedErrMsg: "HTTP 404: Either no autolink with this ID exists for this repository or you are missing admin rights to the repository. (https://api.github.com/repos/OWNER/REPO/autolinks/123)",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reg := &httpmock.Registry{}
reg.Register(
httpmock.REST(
http.MethodGet,
fmt.Sprintf("repos/%s/%s/autolinks/%s", repo.RepoOwner(), repo.RepoName(), tt.id),
),
httpmock.StatusStringResponse(tt.stubStatus, tt.stubRespJSON),
)
defer reg.Verify(t)

autolinkCreator := &AutolinkViewer{
HTTPClient: &http.Client{Transport: reg},
}

autolink, err := autolinkCreator.View(repo, tt.id)

if tt.expectErr {
require.EqualError(t, err, tt.expectedErrMsg)
} else {
require.NoError(t, err)
assert.Equal(t, tt.expectedAutolink, autolink)
}
})
}
}
96 changes: 96 additions & 0 deletions pkg/cmd/repo/autolink/view/view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package view

import (
"fmt"

"github.com/cli/cli/v2/internal/browser"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/repo/autolink/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/spf13/cobra"
)

type viewOptions struct {
BaseRepo func() (ghrepo.Interface, error)
Browser browser.Browser
AutolinkClient AutolinkViewClient
IO *iostreams.IOStreams
Exporter cmdutil.Exporter

ID string
}

type AutolinkViewClient interface {
View(repo ghrepo.Interface, id string) (*shared.Autolink, error)
}

func NewCmdView(f *cmdutil.Factory, runF func(*viewOptions) error) *cobra.Command {
opts := &viewOptions{
Browser: f.Browser,
IO: f.IOStreams,
}

cmd := &cobra.Command{
Use: "view <id>",
Short: "View an autolink reference",
Long: "View an autolink reference for a repository.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
httpClient, err := f.HttpClient()
if err != nil {
return err
}

opts.BaseRepo = f.BaseRepo
opts.ID = args[0]
opts.AutolinkClient = &AutolinkViewer{HTTPClient: httpClient}

if runF != nil {
return runF(opts)
}

return viewRun(opts)
},
}

cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.AutolinkFields)

return cmd
}

func viewRun(opts *viewOptions) error {
repo, err := opts.BaseRepo()
if err != nil {
return err
}

out := opts.IO.Out
cs := opts.IO.ColorScheme()

autolink, err := opts.AutolinkClient.View(repo, opts.ID)

if err != nil {
return fmt.Errorf("%s %w", cs.Red("error viewing autolink:"), err)
}

if opts.Exporter != nil {
return opts.Exporter.Write(opts.IO, autolink)
}

fmt.Fprintf(out, "Autolink in %s\n\n", ghrepo.FullName(repo))

fmt.Fprint(out, cs.Bold("ID: "))
fmt.Fprintln(out, cs.Cyanf("%d", autolink.ID))

fmt.Fprint(out, cs.Bold("Key Prefix: "))
fmt.Fprintln(out, autolink.KeyPrefix)

fmt.Fprint(out, cs.Bold("URL Template: "))
fmt.Fprintln(out, autolink.URLTemplate)

fmt.Fprint(out, cs.Bold("Alphanumeric: "))
fmt.Fprintln(out, autolink.IsAlphanumeric)

return nil
}
Loading