Skip to content
Merged
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
12 changes: 12 additions & 0 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"
"net/http/httputil"
"os"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -1008,6 +1009,17 @@ func (c *Client) ListIncidentsCLI(ctx context.Context, page, pageSize int, sort
}, nil
}

var incidentSeqIDPattern = regexp.MustCompile(`(?i)^INC-(\d+)$`)

// NormalizeIncidentID strips the "INC-" prefix from sequential IDs so the
// bare number can be passed to the API (which accepts UUID, slug, or numeric sequential ID).
func NormalizeIncidentID(id string) string {
if m := incidentSeqIDPattern.FindStringSubmatch(id); m != nil {
return m[1]
}
return id
}

// GetIncidentByID fetches incident detail by ID without requiring updatedAt parameter.
// Does not use cache (stateless).
func (c *Client) GetIncidentByID(ctx context.Context, id string) (*Incident, error) {
Expand Down
25 changes: 25 additions & 0 deletions internal/api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ func TestNewClientWithHTTPS(t *testing.T) {
}
}

func TestNormalizeIncidentID(t *testing.T) {
tests := []struct {
input string
want string
}{
{"INC-42", "42"},
{"INC-1", "1"},
{"inc-99", "99"},
{"Inc-123", "123"},
{"e5923856-6fe8-4a2c-b0eb-cb783e811d06", "e5923856-6fe8-4a2c-b0eb-cb783e811d06"},
{"some-slug", "some-slug"},
{"INC-", "INC-"},
{"INC-abc", "INC-abc"},
{"42", "42"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := NormalizeIncidentID(tt.input)
if got != tt.want {
t.Errorf("NormalizeIncidentID(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}

func TestUserAgentHeader(t *testing.T) {
Version = "1.2.3"

Expand Down
45 changes: 45 additions & 0 deletions internal/cmd/incidents/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,51 @@ func TestRunListNoToken(t *testing.T) {
}
}

func TestRunGetNormalizesSequentialID(t *testing.T) {
setupTestServer(t, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v1/incidents/42" {
t.Errorf("expected path /v1/incidents/42, got %s", r.URL.Path)
}
w.Header().Set("Content-Type", "application/vnd.api+json")
w.Write([]byte(getResponse()))
})

viper.Set("format", "table")
cmd := newTestCmd()

output := captureStdout(t, func() {
err := runGet(cmd, []string{"INC-42"})
if err != nil {
t.Fatalf("runGet returned error: %v", err)
}
})

if !strings.Contains(output, "DB outage") {
t.Errorf("expected output to contain 'DB outage', got: %s", output)
}
}

func TestRunGetAcceptsUUID(t *testing.T) {
uuid := "e5923856-6fe8-4a2c-b0eb-cb783e811d06"
setupTestServer(t, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/v1/incidents/"+uuid {
t.Errorf("expected UUID in path, got %s", r.URL.Path)
}
w.Header().Set("Content-Type", "application/vnd.api+json")
w.Write([]byte(getResponse()))
})

viper.Set("format", "table")
cmd := newTestCmd()

captureStdout(t, func() {
err := runGet(cmd, []string{uuid})
if err != nil {
t.Fatalf("runGet returned error: %v", err)
}
})
}

func TestRunGetAPIError(t *testing.T) {
setupTestServer(t, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
Expand Down
9 changes: 6 additions & 3 deletions internal/cmd/incidents/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"github.com/mattn/go-isatty"
"github.com/spf13/cobra"

"github.com/rootlyhq/rootly-cli/internal/api"
)

var deleteCmd = &cobra.Command{
Expand Down Expand Up @@ -55,7 +57,8 @@ func confirmDelete(prompt string, skipConfirm bool) error {
}

func runDelete(cmd *cobra.Command, args []string) error {
incidentID := args[0]
displayID := args[0]
incidentID := api.NormalizeIncidentID(displayID)

// Get API client
apiClient, err := getAPIClient()
Expand All @@ -65,7 +68,7 @@ func runDelete(cmd *cobra.Command, args []string) error {

// Handle confirmation
skipConfirm, _ := cmd.Flags().GetBool("yes")
prompt := fmt.Sprintf("Delete incident %s? This cannot be undone", incidentID)
prompt := fmt.Sprintf("Delete incident %s? This cannot be undone", displayID)
if err := confirmDelete(prompt, skipConfirm); err != nil {
if err.Error() == "aborted" {
fmt.Fprintln(os.Stderr, "Aborted.")
Expand All @@ -80,7 +83,7 @@ func runDelete(cmd *cobra.Command, args []string) error {
}

// Print success message to stdout
_, _ = fmt.Fprintf(os.Stdout, "Deleted incident %s\n", incidentID)
_, _ = fmt.Fprintf(os.Stdout, "Deleted incident %s\n", displayID)

return nil
}
6 changes: 3 additions & 3 deletions internal/cmd/incidents/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ func init() {
}

func runGet(cmd *cobra.Command, args []string) error {
// Get incident ID from args
id := args[0]
displayID := args[0]
id := api.NormalizeIncidentID(displayID)

// Get API client
apiClient, err := getAPIClient()
Expand All @@ -49,7 +49,7 @@ func runGet(cmd *cobra.Command, args []string) error {
// Call API
incident, err := apiClient.GetIncidentByID(cmd.Context(), id)
if err != nil {
return fmt.Errorf("failed to get incident: %w", err)
return fmt.Errorf("failed to get incident %s: %w", displayID, err)
}

// Get format from viper
Expand Down
6 changes: 4 additions & 2 deletions internal/cmd/incidents/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/rootlyhq/rootly-cli/internal/api"
"github.com/rootlyhq/rootly-cli/internal/printer"
)

Expand Down Expand Up @@ -39,7 +40,8 @@ func init() {
}

func runUpdate(cmd *cobra.Command, args []string) error {
incidentID := args[0]
displayID := args[0]
incidentID := api.NormalizeIncidentID(displayID)

// Get API client
apiClient, err := getAPIClient()
Expand Down Expand Up @@ -78,7 +80,7 @@ func runUpdate(cmd *cobra.Command, args []string) error {
}

// Print success message to stderr
fmt.Fprintf(os.Stderr, "Updated incident %s\n", incidentID)
fmt.Fprintf(os.Stderr, "Updated incident %s\n", displayID)

// Print incident to stdout using configured format
format := viper.GetString("format")
Expand Down
Loading