Skip to content
Draft
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
1 change: 1 addition & 0 deletions pkg/cmd/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The gateway command group provides full access to all Event Gateway resources.`,
addRequestCmdTo(g.cmd)
addAttemptCmdTo(g.cmd)
addMetricsCmdTo(g.cmd)
addIssueCmdTo(g.cmd)

return g
}
1 change: 1 addition & 0 deletions pkg/cmd/helptext.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const (
ResourceEvent = "event"
ResourceRequest = "request"
ResourceAttempt = "attempt"
ResourceIssue = "issue"
)

// Short help (one line) for common commands. Use when the only difference is the resource name.
Expand Down
40 changes: 40 additions & 0 deletions pkg/cmd/issue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cmd

import (
"github.com/spf13/cobra"

"github.com/hookdeck/hookdeck-cli/pkg/validators"
)

type issueCmd struct {
cmd *cobra.Command
}

func newIssueCmd() *issueCmd {
ic := &issueCmd{}

ic.cmd = &cobra.Command{
Use: "issue",
Aliases: []string{"issues"},
Args: validators.NoArgs,
Short: ShortBeta("Manage your issues"),
Long: LongBeta(`Manage Hookdeck issues.

Issues are automatically created when delivery failures, transformation errors,
or backpressure conditions are detected. Use these commands to list, inspect,
update the status of, or dismiss issues.`),
}

ic.cmd.AddCommand(newIssueListCmd().cmd)
ic.cmd.AddCommand(newIssueGetCmd().cmd)
ic.cmd.AddCommand(newIssueUpdateCmd().cmd)
ic.cmd.AddCommand(newIssueDismissCmd().cmd)
ic.cmd.AddCommand(newIssueCountCmd().cmd)

return ic
}

// addIssueCmdTo registers the issue command tree on the given parent.
func addIssueCmdTo(parent *cobra.Command) {
parent.AddCommand(newIssueCmd().cmd)
}
69 changes: 69 additions & 0 deletions pkg/cmd/issue_count.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cmd

import (
"context"
"fmt"
"strconv"

"github.com/spf13/cobra"

"github.com/hookdeck/hookdeck-cli/pkg/validators"
)

type issueCountCmd struct {
cmd *cobra.Command

issueType string
status string
issueTriggerID string
}

func newIssueCountCmd() *issueCountCmd {
ic := &issueCountCmd{}

ic.cmd = &cobra.Command{
Use: "count",
Args: validators.NoArgs,
Short: "Count issues",
Long: `Count issues matching optional filters.

Examples:
hookdeck gateway issue count
hookdeck gateway issue count --type delivery
hookdeck gateway issue count --status OPENED`,
RunE: ic.runIssueCountCmd,
}

ic.cmd.Flags().StringVar(&ic.issueType, "type", "", "Filter by issue type (delivery, transformation, backpressure)")
ic.cmd.Flags().StringVar(&ic.status, "status", "", "Filter by status (OPENED, IGNORED, ACKNOWLEDGED, RESOLVED)")
ic.cmd.Flags().StringVar(&ic.issueTriggerID, "issue-trigger-id", "", "Filter by issue trigger ID")

return ic
}

func (ic *issueCountCmd) runIssueCountCmd(cmd *cobra.Command, args []string) error {
if err := Config.Profile.ValidateAPIKey(); err != nil {
return err
}

client := Config.GetAPIClient()
params := make(map[string]string)

if ic.issueType != "" {
params["type"] = ic.issueType
}
if ic.status != "" {
params["status"] = ic.status
}
if ic.issueTriggerID != "" {
params["issue_trigger_id"] = ic.issueTriggerID
}

resp, err := client.CountIssues(context.Background(), params)
if err != nil {
return fmt.Errorf("failed to count issues: %w", err)
}

fmt.Println(strconv.Itoa(resp.Count))
return nil
}
76 changes: 76 additions & 0 deletions pkg/cmd/issue_dismiss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package cmd

import (
"context"
"encoding/json"
"fmt"

"github.com/spf13/cobra"

"github.com/hookdeck/hookdeck-cli/pkg/validators"
)

type issueDismissCmd struct {
cmd *cobra.Command
force bool
output string
}

func newIssueDismissCmd() *issueDismissCmd {
ic := &issueDismissCmd{}

ic.cmd = &cobra.Command{
Use: "dismiss <issue-id>",
Args: validators.ExactArgs(1),
Short: "Dismiss an issue",
Long: `Dismiss an issue. This sends a DELETE request to the API.

Examples:
hookdeck gateway issue dismiss iss_abc123
hookdeck gateway issue dismiss iss_abc123 --force`,
PreRunE: ic.validateFlags,
RunE: ic.runIssueDismissCmd,
}

ic.cmd.Flags().BoolVar(&ic.force, "force", false, "Dismiss without confirmation")
ic.cmd.Flags().StringVar(&ic.output, "output", "", "Output format (json)")

return ic
}

func (ic *issueDismissCmd) validateFlags(cmd *cobra.Command, args []string) error {
return Config.Profile.ValidateAPIKey()
}

func (ic *issueDismissCmd) runIssueDismissCmd(cmd *cobra.Command, args []string) error {
issueID := args[0]
client := Config.GetAPIClient()
ctx := context.Background()

if !ic.force {
fmt.Printf("Are you sure you want to dismiss issue %s? [y/N]: ", issueID)
var response string
fmt.Scanln(&response)
if response != "y" && response != "Y" {
fmt.Println("Dismiss cancelled.")
return nil
}
}

iss, err := client.DismissIssue(ctx, issueID)
if err != nil {
return fmt.Errorf("failed to dismiss issue: %w", err)
}

if ic.output == "json" {
jsonBytes, err := json.MarshalIndent(iss, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal issue to json: %w", err)
}
fmt.Println(string(jsonBytes))
return nil
}

fmt.Printf(SuccessCheck+" Issue dismissed: %s\n", issueID)
return nil
}
79 changes: 79 additions & 0 deletions pkg/cmd/issue_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cmd

import (
"context"
"encoding/json"
"fmt"
"os"

"github.com/spf13/cobra"

"github.com/hookdeck/hookdeck-cli/pkg/ansi"
"github.com/hookdeck/hookdeck-cli/pkg/validators"
)

type issueGetCmd struct {
cmd *cobra.Command
output string
}

func newIssueGetCmd() *issueGetCmd {
ic := &issueGetCmd{}

ic.cmd = &cobra.Command{
Use: "get <issue-id>",
Args: validators.ExactArgs(1),
Short: ShortGet(ResourceIssue),
Long: `Get detailed information about a specific issue.

Examples:
hookdeck gateway issue get iss_abc123
hookdeck gateway issue get iss_abc123 --output json`,
RunE: ic.runIssueGetCmd,
}

ic.cmd.Flags().StringVar(&ic.output, "output", "", "Output format (json)")

return ic
}

func (ic *issueGetCmd) runIssueGetCmd(cmd *cobra.Command, args []string) error {
if err := Config.Profile.ValidateAPIKey(); err != nil {
return err
}

issueID := args[0]
client := Config.GetAPIClient()
ctx := context.Background()

iss, err := client.GetIssue(ctx, issueID)
if err != nil {
return fmt.Errorf("failed to get issue: %w", err)
}

if ic.output == "json" {
jsonBytes, err := json.MarshalIndent(iss, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal issue to json: %w", err)
}
fmt.Println(string(jsonBytes))
return nil
}

color := ansi.Color(os.Stdout)
statusColor := issueStatusColor(color, string(iss.Status))
fmt.Printf("\n%s\n", color.Bold(iss.ID))
fmt.Printf(" Type: %s\n", string(iss.Type))
fmt.Printf(" Status: %s\n", statusColor)
fmt.Printf(" First seen: %s\n", iss.FirstSeenAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Last seen: %s\n", iss.LastSeenAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Opened at: %s\n", iss.OpenedAt.Format("2006-01-02 15:04:05"))
if iss.DismissedAt != nil {
fmt.Printf(" Dismissed: %s\n", iss.DismissedAt.Format("2006-01-02 15:04:05"))
}
fmt.Printf(" Created: %s\n", iss.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Updated: %s\n", iss.UpdatedAt.Format("2006-01-02 15:04:05"))
fmt.Println()

return nil
}
Loading