Skip to content
Draft
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
73 changes: 53 additions & 20 deletions pkg/cmd/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"os"
"os/signal"
"regexp"
"strings"
"syscall"
"time"
Expand All @@ -23,6 +24,8 @@ import (
"github.com/stripe/stripe-cli/pkg/websocket"
)

var thinEventPattern = regexp.MustCompile(`^v\d+\.`)

const (
webhooksWebSocketFeature = "webhooks"
destinationsWebSocketFeature = "v2_events"
Expand All @@ -34,13 +37,10 @@ type listenCmd struct {
cmd *cobra.Command

forwardURL string
forwardThinURL string
forwardHeaders []string
forwardConnectHeaders []string
forwardConnectURL string
forwardThinConnectURL string
events []string
thinEvents []string
latestAPIVersion bool
livemode bool
useConfiguredWebhooks bool
Expand Down Expand Up @@ -69,8 +69,8 @@ Stripe account.`,
Example: `stripe listen
stripe listen --events charge.captured,charge.updated \
--forward-to localhost:3000/events
stripe listen --thin-events v1.billing.meter.no_meter_found \
--forward-thin-to localhost:3000/thin-events`,
stripe listen --events v1.billing.meter.no_meter_found \
--forward-to localhost:3000/events`,
Annotations: map[string]string{
AIAgentHelpAnnotationKey: " Use `--forward-to` to specify where events are sent, e.g. localhost:4242/webhook.\n" +
" Use `--events` to filter to specific event types, e.g. `--events checkout.session.completed`.\n" +
Expand All @@ -81,13 +81,10 @@ Stripe account.`,
}

lc.cmd.Flags().StringSliceVar(&lc.forwardConnectHeaders, "connect-headers", []string{}, "A comma-separated list of custom headers to forward for Connect. Ex: \"Key1:Value1, Key2:Value2\"")
lc.cmd.Flags().StringSliceVarP(&lc.events, "events", "e", []string{"*"}, "A comma-separated list of specific events to listen for. For a list of all possible events, see: https://stripe.com/docs/api/events/types")
lc.cmd.Flags().StringVarP(&lc.forwardURL, "forward-to", "f", "", "The URL to forward webhook events to")
lc.cmd.Flags().StringSliceVarP(&lc.events, "events", "e", []string{"*"}, "A comma-separated list of specific events to listen for. Supports both snapshot events (e.g. charge.captured) and thin events (e.g. v1.billing.meter.no_meter_found)")
lc.cmd.Flags().StringVarP(&lc.forwardURL, "forward-to", "f", "", "The URL to forward events to")
lc.cmd.Flags().StringSliceVarP(&lc.forwardHeaders, "headers", "H", []string{}, "A comma-separated list of custom headers to forward. Ex: \"Key1:Value1, Key2:Value2\"")
lc.cmd.Flags().StringVarP(&lc.forwardConnectURL, "forward-connect-to", "c", "", "The URL to forward Connect webhook events to (default: same as normal events)")
lc.cmd.Flags().StringSliceVar(&lc.thinEvents, "thin-events", []string{}, "A comma-separated list of thin events to listen for.")
lc.cmd.Flags().StringVar(&lc.forwardThinURL, "forward-thin-to", "", "The URL to forward thin events to")
lc.cmd.Flags().StringVar(&lc.forwardThinConnectURL, "forward-thin-connect-to", "", "The URL to forward thin Connect events to")
lc.cmd.Flags().StringVarP(&lc.forwardConnectURL, "forward-connect-to", "c", "", "The URL to forward Connect events to (default: same as normal events)")
lc.cmd.Flags().BoolVarP(&lc.latestAPIVersion, "latest", "l", false, "Receive events formatted with the latest API version (default: your account's default API version)")
lc.cmd.Flags().BoolVar(&lc.livemode, "live", false, "Receive live events (default: test)")
lc.cmd.Flags().BoolVarP(&lc.printJSON, "print-json", "j", false, "Print full JSON objects to stdout.")
Expand Down Expand Up @@ -179,15 +176,17 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error {
proxyVisitor := lc.createVisitor(logger, lc.format, lc.printJSON)
proxyOutCh := make(chan websocket.IElement)

snapshotEvents, thinEvents := lc.splitEventsByType()

p, err := proxy.Init(ctx, &proxy.Config{
Client: client,
DeviceName: deviceName,
DeviceToken: &lc.deviceToken,
ForwardURL: lc.forwardURL,
ForwardThinURL: lc.forwardThinURL,
ForwardThinURL: lc.forwardURL,
ForwardHeaders: lc.forwardHeaders,
ForwardConnectURL: lc.forwardConnectURL,
ForwardThinConnectURL: lc.forwardThinConnectURL,
ForwardThinConnectURL: lc.forwardConnectURL,
ForwardConnectHeaders: lc.forwardConnectHeaders,
UseConfiguredWebhooks: lc.useConfiguredWebhooks,
WebSocketFeatures: lc.getFeatures(),
Expand All @@ -197,8 +196,8 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error {
Log: logger,
NoWSS: lc.noWSS,
Timeout: lc.timeout,
Events: lc.events,
ThinEvents: lc.thinEvents,
Events: snapshotEvents,
ThinEvents: thinEvents,
OutCh: proxyOutCh,
LoggedInAccountID: accountID,
})
Expand Down Expand Up @@ -359,15 +358,49 @@ func (lc *listenCmd) createVisitor(logger *log.Logger, format string, printJSON
}

func (lc *listenCmd) getFeatures() []string {
features := []string{}
needsSnapshot := false
needsThin := false

for _, e := range lc.events {
if e == "*" {
needsSnapshot = true
needsThin = true
break
}
if isThinEvent(e) {
needsThin = true
} else {
needsSnapshot = true
}
}

if len(lc.events) > 0 {
features := []string{}
if needsSnapshot {
features = append(features, webhooksWebSocketFeature)
}

if len(lc.thinEvents) > 0 {
if needsThin {
features = append(features, destinationsWebSocketFeature)
}

return features
}

// splitEventsByType separates the unified --events list into snapshot and thin
// event lists for the proxy, which still uses two separate channels internally.
func (lc *listenCmd) splitEventsByType() (snapshotEvents []string, thinEvents []string) {
for _, e := range lc.events {
switch {
case e == "*":
snapshotEvents = append(snapshotEvents, "*")
thinEvents = append(thinEvents, "*")
case isThinEvent(e):
thinEvents = append(thinEvents, e)
default:
snapshotEvents = append(snapshotEvents, e)
}
}
return
}

func isThinEvent(eventType string) bool {
return thinEventPattern.MatchString(eventType)
}
Loading