Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func ProxyGetIssueRaw(c *jira.Client, key string) (string, error) {
// ProxyGetIssue uses either a v2 or v3 version of the Jira GET /issue/{key}
// endpoint to fetch the issue details based on configured installation type.
// Defaults to v3 if installation type is not defined in the config.
// Also fetches remote links for the issue.
func ProxyGetIssue(c *jira.Client, key string, opts ...filter.Filter) (*jira.Issue, error) {
var (
iss *jira.Issue
Expand All @@ -121,6 +122,19 @@ func ProxyGetIssue(c *jira.Client, key string, opts ...filter.Filter) (*jira.Iss
iss, err = c.GetIssue(key, opts...)
}

if err != nil {
return iss, err
}

// Fetch remote links for the issue
remoteLinks, err := c.GetIssueRemoteLinks(key)
if err != nil {
// Don't fail the entire request if remote links can't be fetched
// Just log and continue without remote links
remoteLinks = []jira.RemoteLink{}
}
iss.Fields.RemoteLinks = remoteLinks

return iss, err
}

Expand Down
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2
github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/glamour v0.9.1 h1:11dEfiGP8q1BEqvGoIjivuc2rBk+5qEXdPtaQ2WoiCM=
github.com/charmbracelet/glamour v0.9.1 h1:Q7PdJLOx8EoepsXUvW6Puz5WQ3YUElIGQdYKrIpiGLA=
github.com/charmbracelet/glamour v0.9.1/go.mod h1:+SHvIS8qnwhgTpVMiXwn7OfGomSqff1cHBCI8jLOetk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
Expand Down
16 changes: 15 additions & 1 deletion internal/cmd/issue/view/view.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package view

import (
"encoding/json"
"fmt"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -78,7 +79,20 @@ func viewRaw(cmd *cobra.Command, args []string) {
defer s.Stop()

client := api.DefaultClient(debug)
return api.ProxyGetIssueRaw(client, key)

// Fetch the issue with remote links included (same as structured view)
issue, err := api.ProxyGetIssue(client, key)
if err != nil {
return "", err
}

// Convert back to JSON for raw output
rawJSON, err := json.MarshalIndent(issue, "", " ")
if err != nil {
return "", err
}

return string(rawJSON), nil
}()
cmdutil.ExitIfError(err)

Expand Down
48 changes: 48 additions & 0 deletions internal/view/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ func (i Issue) String() string {
if len(i.Data.Fields.IssueLinks) > 0 {
s.WriteString(fmt.Sprintf("\n\n%s\n\n%s\n", i.separator("Linked Issues"), i.linkedIssues()))
}
if len(i.Data.Fields.RemoteLinks) > 0 {
s.WriteString(fmt.Sprintf("\n\n%s\n\n%s\n", i.separator("External Links"), i.remoteLinks()))
}
total := i.Data.Fields.Comment.Total
if total > 0 && i.Options.NumComments > 0 {
sep := fmt.Sprintf("%d Comments", total)
Expand Down Expand Up @@ -160,6 +163,17 @@ func (i Issue) fragments() []fragment {
)
}

if len(i.Data.Fields.RemoteLinks) > 0 {
scraps = append(
scraps,
newBlankFragment(1),
fragment{Body: i.separator("External Links")},
newBlankFragment(2),
fragment{Body: i.remoteLinks()},
newBlankFragment(1),
)
}

if i.Data.Fields.Comment.Total > 0 && i.Options.NumComments > 0 {
scraps = append(
scraps,
Expand Down Expand Up @@ -378,6 +392,40 @@ func (i Issue) linkedIssues() string {
return linked.String()
}

func (i Issue) remoteLinks() string {
if len(i.Data.Fields.RemoteLinks) == 0 {
return ""
}

var (
remote strings.Builder
maxTitleLen int
summaryLen = defaultSummaryLength
)

// Calculate max lengths for formatting
for _, link := range i.Data.Fields.RemoteLinks {
maxTitleLen = max(len(link.Object.Title), maxTitleLen)
}

if maxTitleLen < summaryLen {
summaryLen = maxTitleLen
}

remote.WriteString("\n")
for _, link := range i.Data.Fields.RemoteLinks {
remote.WriteString(
fmt.Sprintf(
" %s\n %s\n\n",
coloredOut(shortenAndPad(link.Object.Title, summaryLen), color.FgCyan, color.Bold),
coloredOut(link.Object.URL, color.FgBlue, color.Underline),
),
)
}

return remote.String()
}

func (i Issue) comments() []issueComment {
total := i.Data.Fields.Comment.Total
comments := make([]issueComment, 0, total)
Expand Down
31 changes: 31 additions & 0 deletions pkg/jira/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,37 @@ func (c *Client) RemoteLinkIssue(issueID, title, url string) error {
return nil
}

// GetIssueRemoteLinks fetches remote links for an issue using GET /issue/{issueId}/remotelink endpoint.
func (c *Client) GetIssueRemoteLinks(issueID string) ([]RemoteLink, error) {
path := fmt.Sprintf("/issue/%s/remotelink", issueID)

res, err := c.GetV2(context.Background(), path, nil)
if err != nil {
return nil, err
}
if res == nil {
return nil, ErrEmptyResponse
}
defer func() { _ = res.Body.Close() }()

if res.StatusCode != http.StatusOK {
return nil, formatUnexpectedResponse(res)
}

body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}

var remoteLinks []RemoteLink
err = json.Unmarshal(body, &remoteLinks)
if err != nil {
return nil, err
}

return remoteLinks, nil
}

// WatchIssue adds user as a watcher using v2 version of the POST /issue/{key}/watchers endpoint.
func (c *Client) WatchIssue(key, watcher string) error {
return c.watchIssue(key, watcher, apiVersion3)
Expand Down
15 changes: 13 additions & 2 deletions pkg/jira/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ type IssueFields struct {
InwardIssue *Issue `json:"inwardIssue,omitempty"`
OutwardIssue *Issue `json:"outwardIssue,omitempty"`
} `json:"issueLinks"`
Created string `json:"created"`
Updated string `json:"updated"`
RemoteLinks []RemoteLink `json:"remoteLinks,omitempty"`
Created string `json:"created"`
Updated string `json:"updated"`
}

// Field holds field info.
Expand Down Expand Up @@ -165,6 +166,16 @@ type IssueLinkType struct {
Outward string `json:"outward"`
}

// RemoteLink holds remote link info.
type RemoteLink struct {
ID int `json:"id"`
Self string `json:"self"`
Object struct {
URL string `json:"url"`
Title string `json:"title"`
} `json:"object"`
}

// Sprint holds sprint info.
type Sprint struct {
ID int `json:"id"`
Expand Down
Loading