From 40e76a403eab7c2c445e532fcbce8494b9cef78a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:54:53 +0000 Subject: [PATCH 01/29] docs(api): update notification parameter descriptions --- .stats.yml | 4 ++-- pkg/cmd/notification.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 998581c..effa635 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-0f56d7860a1be3ffaea5dea291b20ac92e31c94d54e41cc8a30462dacf2f2d37.yml -openapi_spec_hash: 7e7c6f963e83c5f626c09efa322f476a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-3f78581b4e078a1f620d9f587f18d77bcde6d20f56b0e4ae798648f4236494fb.yml +openapi_spec_hash: 6bd33e0396d85e11bb46f0d549af93a3 config_hash: afcc4f6f8c33ca3f338589e32e086f56 diff --git a/pkg/cmd/notification.go b/pkg/cmd/notification.go index b1b75d5..a02e54a 100644 --- a/pkg/cmd/notification.go +++ b/pkg/cmd/notification.go @@ -22,7 +22,7 @@ var notificationsCreate = requestflag.WithInnerFlags(cli.Command{ Flags: []cli.Flag{ &requestflag.Flag[map[string]any]{ Name: "notification", - Usage: "Full document shape used in POST and PUT request bodies, and returned inside the GET response envelope.", + Usage: "Core template fields used in POST and PUT request bodies (nested under a `notification` key) and returned at the top level in responses.", Required: true, BodyPath: "notification", }, @@ -308,7 +308,7 @@ var notificationsReplace = requestflag.WithInnerFlags(cli.Command{ }, &requestflag.Flag[map[string]any]{ Name: "notification", - Usage: "Full document shape used in POST and PUT request bodies, and returned inside the GET response envelope.", + Usage: "Core template fields used in POST and PUT request bodies (nested under a `notification` key) and returned at the top level in responses.", Required: true, BodyPath: "notification", }, From 09aeebd03bb1402bd278940cdeb8ea69b01517b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 02:23:52 +0000 Subject: [PATCH 02/29] chore(cli): fall back to JSON when using default "explore" with non-TTY --- cmd/courier/main.go | 2 +- pkg/cmd/audience.go | 12 ++++-- pkg/cmd/auditevent.go | 6 ++- pkg/cmd/auth.go | 3 +- pkg/cmd/automation.go | 3 +- pkg/cmd/automationinvoke.go | 6 ++- pkg/cmd/brand.go | 12 ++++-- pkg/cmd/bulk.go | 9 ++-- pkg/cmd/cmdutil.go | 29 ++++++++++--- pkg/cmd/cmdutil_test.go | 70 +++++++++++++++++++++++++++++++- pkg/cmd/inbound.go | 3 +- pkg/cmd/journey.go | 6 ++- pkg/cmd/list.go | 6 ++- pkg/cmd/listsubscription.go | 3 +- pkg/cmd/message.go | 15 ++++--- pkg/cmd/notification.go | 27 ++++++++---- pkg/cmd/notificationcheck.go | 6 ++- pkg/cmd/profile.go | 9 ++-- pkg/cmd/profilelist.go | 9 ++-- pkg/cmd/provider.go | 12 ++++-- pkg/cmd/providercatalog.go | 3 +- pkg/cmd/routingstrategy.go | 15 ++++--- pkg/cmd/send.go | 3 +- pkg/cmd/tenant.go | 12 ++++-- pkg/cmd/tenanttemplate.go | 12 ++++-- pkg/cmd/tenanttemplateversion.go | 3 +- pkg/cmd/translation.go | 3 +- pkg/cmd/userpreference.go | 9 ++-- pkg/cmd/usertenant.go | 3 +- pkg/cmd/usertoken.go | 6 ++- 30 files changed, 237 insertions(+), 80 deletions(-) diff --git a/cmd/courier/main.go b/cmd/courier/main.go index d1de87e..cc55f81 100644 --- a/cmd/courier/main.go +++ b/cmd/courier/main.go @@ -43,7 +43,7 @@ func main() { fmt.Fprintf(os.Stderr, "%s %q: %d %s\n", apierr.Request.Method, apierr.Request.URL, apierr.Response.StatusCode, http.StatusText(apierr.Response.StatusCode)) format := app.String("format-error") json := gjson.Parse(apierr.RawJSON()) - show_err := cmd.ShowJSON(os.Stdout, "Error", json, format, app.String("transform-error")) + show_err := cmd.ShowJSON(os.Stdout, os.Stderr, "Error", json, format, app.IsSet("format-error"), app.String("transform-error")) if show_err != nil { // Just print the original error: fmt.Fprintf(os.Stderr, "%s\n", err.Error()) diff --git a/pkg/cmd/audience.go b/pkg/cmd/audience.go index 9867947..1d41d96 100644 --- a/pkg/cmd/audience.go +++ b/pkg/cmd/audience.go @@ -150,8 +150,9 @@ func handleAudiencesRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "audiences retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "audiences retrieve", obj, format, explicitFormat, transform) } func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { @@ -192,8 +193,9 @@ func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "audiences update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "audiences update", obj, format, explicitFormat, transform) } func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { @@ -226,8 +228,9 @@ func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "audiences list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "audiences list", obj, format, explicitFormat, transform) } func handleAudiencesDelete(ctx context.Context, cmd *cli.Command) error { @@ -293,6 +296,7 @@ func handleAudiencesListMembers(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "audiences list-members", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "audiences list-members", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/auditevent.go b/pkg/cmd/auditevent.go index 96fcdca..4524694 100644 --- a/pkg/cmd/auditevent.go +++ b/pkg/cmd/auditevent.go @@ -75,8 +75,9 @@ func handleAuditEventsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "audit-events retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "audit-events retrieve", obj, format, explicitFormat, transform) } func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { @@ -109,6 +110,7 @@ func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "audit-events list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "audit-events list", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/auth.go b/pkg/cmd/auth.go index 8926ff2..5a83913 100644 --- a/pkg/cmd/auth.go +++ b/pkg/cmd/auth.go @@ -67,6 +67,7 @@ func handleAuthIssueToken(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "auth issue-token", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "auth issue-token", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/automation.go b/pkg/cmd/automation.go index d486175..bd670fb 100644 --- a/pkg/cmd/automation.go +++ b/pkg/cmd/automation.go @@ -66,6 +66,7 @@ func handleAutomationsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "automations list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "automations list", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/automationinvoke.go b/pkg/cmd/automationinvoke.go index e3c4921..2e65e9a 100644 --- a/pkg/cmd/automationinvoke.go +++ b/pkg/cmd/automationinvoke.go @@ -126,8 +126,9 @@ func handleAutomationsInvokeInvokeAdHoc(ctx context.Context, cmd *cli.Command) e obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "automations:invoke invoke-ad-hoc", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "automations:invoke invoke-ad-hoc", obj, format, explicitFormat, transform) } func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Command) error { @@ -168,6 +169,7 @@ func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Comma obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "automations:invoke invoke-by-template", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "automations:invoke invoke-by-template", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/brand.go b/pkg/cmd/brand.go index 03a6d7b..9e237ac 100644 --- a/pkg/cmd/brand.go +++ b/pkg/cmd/brand.go @@ -185,8 +185,9 @@ func handleBrandsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "brands create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "brands create", obj, format, explicitFormat, transform) } func handleBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -220,8 +221,9 @@ func handleBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "brands retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "brands retrieve", obj, format, explicitFormat, transform) } func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -262,8 +264,9 @@ func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "brands update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "brands update", obj, format, explicitFormat, transform) } func handleBrandsList(ctx context.Context, cmd *cli.Command) error { @@ -296,8 +299,9 @@ func handleBrandsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "brands list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "brands list", obj, format, explicitFormat, transform) } func handleBrandsDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/bulk.go b/pkg/cmd/bulk.go index 3663ff9..cfa0a5a 100644 --- a/pkg/cmd/bulk.go +++ b/pkg/cmd/bulk.go @@ -219,8 +219,9 @@ func handleBulkCreateJob(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "bulk create-job", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "bulk create-job", obj, format, explicitFormat, transform) } func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { @@ -261,8 +262,9 @@ func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "bulk list-users", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "bulk list-users", obj, format, explicitFormat, transform) } func handleBulkRetrieveJob(ctx context.Context, cmd *cli.Command) error { @@ -296,8 +298,9 @@ func handleBulkRetrieveJob(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "bulk retrieve-job", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "bulk retrieve-job", obj, format, explicitFormat, transform) } func handleBulkRunJob(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index d74609f..0eaacf4 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -354,8 +354,13 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format } } -// Display JSON to the user in various different formats -func ShowJSON(out *os.File, title string, res gjson.Result, format string, transform string) error { +const warningExploreNotSupported = "Warning: Output format 'explore' not supported for non-terminal output; falling back to 'json'\n" + +// Display JSON to the user in various different formats. The explicitFormat parameter indicates +// whether the format was explicitly set by the user (via --format), which controls whether we +// silently fall back to json when explore is requested on non-terminal output. Warnings are +// written to stderr. +func ShowJSON(out *os.File, stderr io.Writer, title string, res gjson.Result, format string, explicitFormat bool, transform string) error { if transform != "" { transformed := res.Get(transform) if transformed.Exists() { @@ -365,8 +370,14 @@ func ShowJSON(out *os.File, title string, res gjson.Result, format string, trans switch strings.ToLower(format) { case "auto": - return ShowJSON(out, title, res, "json", "") + return ShowJSON(out, stderr, title, res, "json", explicitFormat, "") case "explore": + if !isTerminal(out) { + if explicitFormat { + fmt.Fprint(stderr, warningExploreNotSupported) + } + return ShowJSON(out, stderr, title, res, "json", explicitFormat, transform) + } return jsonview.ExploreJSON(title, res) default: bytes, err := formatJSON(out, title, res, format, transform) @@ -391,9 +402,15 @@ type hasRawJSON interface { // For an iterator over different value types, display its values to the user in // different formats. // -1 is used to signal no limit of items to display -func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterator[T], format string, transform string, itemsToDisplay int64) error { +func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, iter jsonview.Iterator[T], format string, explicitFormat bool, transform string, itemsToDisplay int64) error { if format == "explore" { - return jsonview.ExploreJSONStream(title, iter) + if isTerminal(stdout) { + return jsonview.ExploreJSONStream(title, iter) + } + if explicitFormat { + fmt.Fprint(stderr, warningExploreNotSupported) + } + format = "json" } terminalWidth, terminalHeight, err := term.GetSize(os.Stdout.Fd()) @@ -466,7 +483,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat } obj = gjson.ParseBytes(jsonData) } - if err := ShowJSON(pager, title, obj, format, transform); err != nil { + if err := ShowJSON(pager, stderr, title, obj, format, explicitFormat, transform); err != nil { return err } itemsToDisplay -= 1 diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 4a7c832..0dcbab0 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -231,6 +231,73 @@ func TestShowJSONIterator(t *testing.T) { }) } +func TestExploreFallback(t *testing.T) { + t.Parallel() + + t.Run("ShowJSONFallsBackToJsonOnNonTTY", func(t *testing.T) { + t.Parallel() + + // os.Pipe() produces a *os.File that isn't a terminal, so explore should fall back. + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + + var stderr bytes.Buffer + res := gjson.Parse(`{"id":"abc"}`) + err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + w.Close() + require.NoError(t, err) + + var buf bytes.Buffer + _, _ = buf.ReadFrom(r) + assert.Contains(t, buf.String(), `"id"`) + assert.Contains(t, buf.String(), `"abc"`) + }) + + t.Run("ShowJSONIteratorFallsBackToJsonOnNonTTY", func(t *testing.T) { + t.Parallel() + + iter := &sliceIterator[map[string]any]{items: []map[string]any{ + {"id": "abc"}, + }} + captured := captureShowJSONIterator(t, iter, "explore", "", -1) + assert.Contains(t, captured, `"id"`) + assert.Contains(t, captured, `"abc"`) + }) + + t.Run("ShowJSONWarnsWhenExplicitFormatOnNonTTY", func(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + + var stderr bytes.Buffer + res := gjson.Parse(`{"id":"abc"}`) + err = ShowJSON(w, &stderr, "test", res, "explore", true, "") + w.Close() + require.NoError(t, err) + + assert.Equal(t, warningExploreNotSupported, stderr.String()) + }) + + t.Run("ShowJSONSilentWhenDefaultFormatOnNonTTY", func(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + + var stderr bytes.Buffer + res := gjson.Parse(`{"id":"abc"}`) + err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + w.Close() + require.NoError(t, err) + + assert.Empty(t, stderr.String(), "no warning expected when format was not explicit") + }) +} + // sliceIterator is a simple iterator over a slice for testing. type sliceIterator[T any] struct { index int @@ -260,7 +327,8 @@ func captureShowJSONIterator[T any](t *testing.T, iter jsonview.Iterator[T], for require.NoError(t, err) defer r.Close() - err = ShowJSONIterator(w, "test", iter, format, transform, itemsToDisplay) + var stderr bytes.Buffer + err = ShowJSONIterator(w, &stderr, "test", iter, format, false, transform, itemsToDisplay) w.Close() require.NoError(t, err) diff --git a/pkg/cmd/inbound.go b/pkg/cmd/inbound.go index 4d47c00..a120485 100644 --- a/pkg/cmd/inbound.go +++ b/pkg/cmd/inbound.go @@ -83,6 +83,7 @@ func handleInboundTrackEvent(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "inbound track-event", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "inbound track-event", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/journey.go b/pkg/cmd/journey.go index c9ee498..22c54b1 100644 --- a/pkg/cmd/journey.go +++ b/pkg/cmd/journey.go @@ -95,8 +95,9 @@ func handleJourneysList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "journeys list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "journeys list", obj, format, explicitFormat, transform) } func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { @@ -137,6 +138,7 @@ func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "journeys invoke", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "journeys invoke", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/list.go b/pkg/cmd/list.go index 9bd98f6..fd32c45 100644 --- a/pkg/cmd/list.go +++ b/pkg/cmd/list.go @@ -142,8 +142,9 @@ func handleListsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "lists retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "lists retrieve", obj, format, explicitFormat, transform) } func handleListsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -208,8 +209,9 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "lists list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "lists list", obj, format, explicitFormat, transform) } func handleListsDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/listsubscription.go b/pkg/cmd/listsubscription.go index 683c80a..26f0da7 100644 --- a/pkg/cmd/listsubscription.go +++ b/pkg/cmd/listsubscription.go @@ -183,8 +183,9 @@ func handleListsSubscriptionsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "lists:subscriptions list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "lists:subscriptions list", obj, format, explicitFormat, transform) } func handleListsSubscriptionsAdd(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 5a4462e..bf44193 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -187,8 +187,9 @@ func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages retrieve", obj, format, explicitFormat, transform) } func handleMessagesList(ctx context.Context, cmd *cli.Command) error { @@ -221,8 +222,9 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages list", obj, format, explicitFormat, transform) } func handleMessagesCancel(ctx context.Context, cmd *cli.Command) error { @@ -256,8 +258,9 @@ func handleMessagesCancel(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages cancel", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages cancel", obj, format, explicitFormat, transform) } func handleMessagesContent(ctx context.Context, cmd *cli.Command) error { @@ -291,8 +294,9 @@ func handleMessagesContent(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages content", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages content", obj, format, explicitFormat, transform) } func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { @@ -333,6 +337,7 @@ func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "messages history", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "messages history", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/notification.go b/pkg/cmd/notification.go index a02e54a..99e733f 100644 --- a/pkg/cmd/notification.go +++ b/pkg/cmd/notification.go @@ -404,8 +404,9 @@ func handleNotificationsCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications create", obj, format, explicitFormat, transform) } func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -446,8 +447,9 @@ func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications retrieve", obj, format, explicitFormat, transform) } func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { @@ -480,8 +482,9 @@ func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications list", obj, format, explicitFormat, transform) } func handleNotificationsArchive(ctx context.Context, cmd *cli.Command) error { @@ -547,8 +550,9 @@ func handleNotificationsListVersions(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications list-versions", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications list-versions", obj, format, explicitFormat, transform) } func handleNotificationsPublish(ctx context.Context, cmd *cli.Command) error { @@ -621,8 +625,9 @@ func handleNotificationsPutContent(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications put-content", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications put-content", obj, format, explicitFormat, transform) } func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error { @@ -665,8 +670,9 @@ func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications put-element", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications put-element", obj, format, explicitFormat, transform) } func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { @@ -709,8 +715,9 @@ func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications put-locale", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications put-locale", obj, format, explicitFormat, transform) } func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { @@ -751,8 +758,9 @@ func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications replace", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications replace", obj, format, explicitFormat, transform) } func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) error { @@ -793,6 +801,7 @@ func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) e obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications retrieve-content", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications retrieve-content", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/notificationcheck.go b/pkg/cmd/notificationcheck.go index 11eb60d..4c3d841 100644 --- a/pkg/cmd/notificationcheck.go +++ b/pkg/cmd/notificationcheck.go @@ -131,8 +131,9 @@ func handleNotificationsChecksUpdate(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications:checks update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications:checks update", obj, format, explicitFormat, transform) } func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error { @@ -175,8 +176,9 @@ func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "notifications:checks list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "notifications:checks list", obj, format, explicitFormat, transform) } func handleNotificationsChecksDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/profile.go b/pkg/cmd/profile.go index 56eca9f..cfdc9e9 100644 --- a/pkg/cmd/profile.go +++ b/pkg/cmd/profile.go @@ -157,8 +157,9 @@ func handleProfilesCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "profiles create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "profiles create", obj, format, explicitFormat, transform) } func handleProfilesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -192,8 +193,9 @@ func handleProfilesRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "profiles retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "profiles retrieve", obj, format, explicitFormat, transform) } func handleProfilesUpdate(ctx context.Context, cmd *cli.Command) error { @@ -291,6 +293,7 @@ func handleProfilesReplace(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "profiles replace", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "profiles replace", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/profilelist.go b/pkg/cmd/profilelist.go index 8155ed9..2ac2164 100644 --- a/pkg/cmd/profilelist.go +++ b/pkg/cmd/profilelist.go @@ -116,8 +116,9 @@ func handleProfilesListsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "profiles:lists retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "profiles:lists retrieve", obj, format, explicitFormat, transform) } func handleProfilesListsDelete(ctx context.Context, cmd *cli.Command) error { @@ -151,8 +152,9 @@ func handleProfilesListsDelete(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "profiles:lists delete", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "profiles:lists delete", obj, format, explicitFormat, transform) } func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { @@ -193,6 +195,7 @@ func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "profiles:lists subscribe", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "profiles:lists subscribe", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/provider.go b/pkg/cmd/provider.go index 3a237aa..87df70b 100644 --- a/pkg/cmd/provider.go +++ b/pkg/cmd/provider.go @@ -154,8 +154,9 @@ func handleProvidersCreate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "providers create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "providers create", obj, format, explicitFormat, transform) } func handleProvidersRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -189,8 +190,9 @@ func handleProvidersRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "providers retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "providers retrieve", obj, format, explicitFormat, transform) } func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { @@ -231,8 +233,9 @@ func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "providers update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "providers update", obj, format, explicitFormat, transform) } func handleProvidersList(ctx context.Context, cmd *cli.Command) error { @@ -265,8 +268,9 @@ func handleProvidersList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "providers list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "providers list", obj, format, explicitFormat, transform) } func handleProvidersDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/providercatalog.go b/pkg/cmd/providercatalog.go index 5eda0fa..e8b42fa 100644 --- a/pkg/cmd/providercatalog.go +++ b/pkg/cmd/providercatalog.go @@ -70,6 +70,7 @@ func handleProvidersCatalogList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "providers:catalog list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "providers:catalog list", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/routingstrategy.go b/pkg/cmd/routingstrategy.go index f03d2fe..dbecbd8 100644 --- a/pkg/cmd/routingstrategy.go +++ b/pkg/cmd/routingstrategy.go @@ -225,8 +225,9 @@ func handleRoutingStrategiesCreate(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "routing-strategies create", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "routing-strategies create", obj, format, explicitFormat, transform) } func handleRoutingStrategiesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -260,8 +261,9 @@ func handleRoutingStrategiesRetrieve(ctx context.Context, cmd *cli.Command) erro obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "routing-strategies retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "routing-strategies retrieve", obj, format, explicitFormat, transform) } func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { @@ -294,8 +296,9 @@ func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "routing-strategies list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "routing-strategies list", obj, format, explicitFormat, transform) } func handleRoutingStrategiesArchive(ctx context.Context, cmd *cli.Command) error { @@ -361,8 +364,9 @@ func handleRoutingStrategiesListNotifications(ctx context.Context, cmd *cli.Comm obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "routing-strategies list-notifications", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "routing-strategies list-notifications", obj, format, explicitFormat, transform) } func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error { @@ -403,6 +407,7 @@ func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "routing-strategies replace", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "routing-strategies replace", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/send.go b/pkg/cmd/send.go index 3478ded..fe3117c 100644 --- a/pkg/cmd/send.go +++ b/pkg/cmd/send.go @@ -123,6 +123,7 @@ func handleSendMessage(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "send message", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "send message", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/tenant.go b/pkg/cmd/tenant.go index 52c014a..50cd6b1 100644 --- a/pkg/cmd/tenant.go +++ b/pkg/cmd/tenant.go @@ -174,8 +174,9 @@ func handleTenantsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants retrieve", obj, format, explicitFormat, transform) } func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -216,8 +217,9 @@ func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants update", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants update", obj, format, explicitFormat, transform) } func handleTenantsList(ctx context.Context, cmd *cli.Command) error { @@ -250,8 +252,9 @@ func handleTenantsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants list", obj, format, explicitFormat, transform) } func handleTenantsDelete(ctx context.Context, cmd *cli.Command) error { @@ -317,6 +320,7 @@ func handleTenantsListUsers(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants list-users", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants list-users", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/tenanttemplate.go b/pkg/cmd/tenanttemplate.go index b5bbee3..593930c 100644 --- a/pkg/cmd/tenanttemplate.go +++ b/pkg/cmd/tenanttemplate.go @@ -170,8 +170,9 @@ func handleTenantsTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants:templates retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants:templates retrieve", obj, format, explicitFormat, transform) } func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { @@ -212,8 +213,9 @@ func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants:templates list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants:templates list", obj, format, explicitFormat, transform) } func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error { @@ -256,8 +258,9 @@ func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants:templates publish", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants:templates publish", obj, format, explicitFormat, transform) } func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error { @@ -300,6 +303,7 @@ func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants:templates replace", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants:templates replace", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/tenanttemplateversion.go b/pkg/cmd/tenanttemplateversion.go index 0f50a89..90d23ed 100644 --- a/pkg/cmd/tenanttemplateversion.go +++ b/pkg/cmd/tenanttemplateversion.go @@ -78,6 +78,7 @@ func handleTenantsTemplatesVersionsRetrieve(ctx context.Context, cmd *cli.Comman obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "tenants:templates:versions retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "tenants:templates:versions retrieve", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/translation.go b/pkg/cmd/translation.go index 86fa59e..1c65d74 100644 --- a/pkg/cmd/translation.go +++ b/pkg/cmd/translation.go @@ -96,8 +96,9 @@ func handleTranslationsRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "translations retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "translations retrieve", obj, format, explicitFormat, transform) } func handleTranslationsUpdate(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/userpreference.go b/pkg/cmd/userpreference.go index 121c8b8..7a53d7f 100644 --- a/pkg/cmd/userpreference.go +++ b/pkg/cmd/userpreference.go @@ -140,8 +140,9 @@ func handleUsersPreferencesRetrieve(ctx context.Context, cmd *cli.Command) error obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "users:preferences retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "users:preferences retrieve", obj, format, explicitFormat, transform) } func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) error { @@ -184,8 +185,9 @@ func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "users:preferences retrieve-topic", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "users:preferences retrieve-topic", obj, format, explicitFormat, transform) } func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Command) error { @@ -228,6 +230,7 @@ func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Com obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "users:preferences update-or-create-topic", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "users:preferences update-or-create-topic", obj, format, explicitFormat, transform) } diff --git a/pkg/cmd/usertenant.go b/pkg/cmd/usertenant.go index 2d2f153..3528b1a 100644 --- a/pkg/cmd/usertenant.go +++ b/pkg/cmd/usertenant.go @@ -173,8 +173,9 @@ func handleUsersTenantsList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "users:tenants list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "users:tenants list", obj, format, explicitFormat, transform) } func handleUsersTenantsAddMultiple(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/usertoken.go b/pkg/cmd/usertoken.go index 877f05e..15ed67d 100644 --- a/pkg/cmd/usertoken.go +++ b/pkg/cmd/usertoken.go @@ -259,8 +259,9 @@ func handleUsersTokensRetrieve(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "users:tokens retrieve", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "users:tokens retrieve", obj, format, explicitFormat, transform) } func handleUsersTokensUpdate(ctx context.Context, cmd *cli.Command) error { @@ -328,8 +329,9 @@ func handleUsersTokensList(ctx context.Context, cmd *cli.Command) error { obj := gjson.ParseBytes(res) format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, "users:tokens list", obj, format, transform) + return ShowJSON(os.Stdout, os.Stderr, "users:tokens list", obj, format, explicitFormat, transform) } func handleUsersTokensDelete(ctx context.Context, cmd *cli.Command) error { From 4c5210271156ddab475383aa6d512d915c43e7b6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 02:25:20 +0000 Subject: [PATCH 03/29] feat(cli): alias parameters in data with `x-stainless-cli-data-alias` --- internal/requestflag/innerflag.go | 19 ++++++++++-- internal/requestflag/requestflag.go | 9 ++++++ pkg/cmd/flagoptions.go | 47 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/internal/requestflag/innerflag.go b/internal/requestflag/innerflag.go index 102624f..eeeb8bc 100644 --- a/internal/requestflag/innerflag.go +++ b/internal/requestflag/innerflag.go @@ -22,14 +22,29 @@ type InnerFlag[ Aliases []string // aliases that are allowed for this flag Validator func(T) error // custom function to validate this flag value - OuterFlag cli.Flag // The flag on which this inner flag will set values - InnerField string // The inner field which this flag will set + OuterFlag cli.Flag // The flag on which this inner flag will set values + InnerField string // The inner field which this flag will set + DataAliases []string // alternate names recognized in YAML values passed as the outer flag +} + +// GetDataAliases returns the aliases recognized when parsing inner field keys from piped or flag YAML. +func (f *InnerFlag[T]) GetDataAliases() []string { + return f.DataAliases +} + +// GetInnerField returns the API field name that this inner flag sets on its outer flag's value. +// For example, the flag --parent.foo targeting a parameter whose OpenAPI property name is "foo" +// would return "foo". This is distinct from the flag's CLI name and from any DataAliases entries. +func (f *InnerFlag[T]) GetInnerField() string { + return f.InnerField } type HasOuterFlag interface { cli.Flag SetOuterFlag(cli.Flag) GetOuterFlag() cli.Flag + GetInnerField() string + GetDataAliases() []string } func (f *InnerFlag[T]) SetOuterFlag(flag cli.Flag) { diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go index bdef64f..bfaf064 100644 --- a/internal/requestflag/requestflag.go +++ b/internal/requestflag/requestflag.go @@ -48,6 +48,10 @@ type Flag[ // binary` in the OpenAPI spec. FileInput bool + // DataAliases is a list of alternate names for this parameter recognized when parsing piped YAML/JSON + // input. Values keyed by any alias are translated to the canonical API name before being sent. + DataAliases []string + // unexported fields for internal use count int // number of times the flag has been set hasBeenSet bool // whether the flag has been set from env or file @@ -65,6 +69,7 @@ type InRequest interface { GetBodyPath() string IsBodyRoot() bool IsFileInput() bool + GetDataAliases() []string } func (f Flag[T]) GetQueryPath() string { @@ -87,6 +92,10 @@ func (f Flag[T]) IsFileInput() bool { return f.FileInput } +func (f Flag[T]) GetDataAliases() []string { + return f.DataAliases +} + // The values that will be sent in different parts of a request. type RequestContents struct { Queries map[string]any diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index a64588a..5c70efe 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -309,6 +309,12 @@ func flagOptions( requestContents := requestflag.ExtractRequestContents(cmd) + // Translate inner-field aliases in YAML values that came from flags (e.g. + // `--parent '{"alias": val}'` resolving to the canonical inner field). + if bodyMap, ok := requestContents.Body.(map[string]any); ok { + applyDataAliases(cmd, bodyMap) + } + stdinConsumedByPipe := false if (bodyType == MultipartFormEncoded || bodyType == ApplicationJSON) && !ignoreStdin && isInputPiped() { pipeData, err := io.ReadAll(os.Stdin) @@ -323,6 +329,7 @@ func flagOptions( return nil, fmt.Errorf("Failed to parse piped data as YAML/JSON:\n%w", err) } if bodyMap, ok := bodyData.(map[string]any); ok { + applyDataAliases(cmd, bodyMap) if flagMap, ok := requestContents.Body.(map[string]any); ok { maps.Copy(bodyMap, flagMap) requestContents.Body = bodyMap @@ -485,6 +492,46 @@ func flagOptions( // as a file path without needing the "@" prefix. type FilePathValue string +// applyDataAliases rewrites keys in a body map based on flag `DataAliases` metadata. For top-level flags, +// `{alias: value}` becomes `{canonical: value}`. For inner flags (those registered under an outer flag +// via WithInnerFlags), the alias translation is also applied to the nested map under the outer flag's +// body path, so values like `--parent '{"alias": val}'` resolve to the canonical inner field name. +func applyDataAliases(cmd *cli.Command, bodyMap map[string]any) { + for _, flag := range cmd.Flags { + // Inner flags: rewrite aliases inside the nested map under the outer flag's body path. + if inner, ok := flag.(requestflag.HasOuterFlag); ok { + outer, outerOk := inner.GetOuterFlag().(requestflag.InRequest) + if !outerOk { + continue + } + if nested, ok := bodyMap[outer.GetBodyPath()].(map[string]any); ok && inner.GetInnerField() != "" { + rewriteAliases(nested, inner.GetInnerField(), inner.GetDataAliases()) + } + continue + } + // Top-level flags: rewrite aliases in the body map. + if inReq, ok := flag.(requestflag.InRequest); ok && inReq.GetBodyPath() != "" { + rewriteAliases(bodyMap, inReq.GetBodyPath(), inReq.GetDataAliases()) + } + } +} + +// rewriteAliases replaces each alias key in m with the canonical key, preserving the value. The +// "canonical" key is the name the API itself expects (the OpenAPI property/field name) — e.g. for +// a top-level flag, the parameter's BodyPath; for an inner flag, the inner field name. Aliases are +// the user-facing alternate names declared via x-stainless-cli-data-alias. +func rewriteAliases(m map[string]any, canonical string, aliases []string) { + for _, alias := range aliases { + if alias == "" || alias == canonical { + continue + } + if val, exists := m[alias]; exists { + m[canonical] = val + delete(m, alias) + } + } +} + // wrapFileInputValues replaces string values for FileInput flags (type: string, format: binary) with // FilePathValue sentinel values. embedFilesValue recognizes FilePathValue and reads the file contents // directly, so the user doesn't need to type the "@" prefix. This handles both values set via explicit From 389c8207c4c54f657dfd310ce5ab1d8d751cc357 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 02:18:58 +0000 Subject: [PATCH 04/29] chore(cli): switch long lists of positional args over to param structs --- cmd/courier/main.go | 7 ++- pkg/cmd/audience.go | 29 +++++++++-- pkg/cmd/auditevent.go | 15 ++++-- pkg/cmd/auth.go | 8 ++- pkg/cmd/automation.go | 8 ++- pkg/cmd/automationinvoke.go | 15 ++++-- pkg/cmd/brand.go | 29 +++++++++-- pkg/cmd/bulk.go | 22 +++++++-- pkg/cmd/cmdutil.go | 84 +++++++++++++++++++++----------- pkg/cmd/cmdutil_test.go | 31 ++++++++++-- pkg/cmd/inbound.go | 8 ++- pkg/cmd/journey.go | 15 ++++-- pkg/cmd/list.go | 15 ++++-- pkg/cmd/listsubscription.go | 8 ++- pkg/cmd/message.go | 36 +++++++++++--- pkg/cmd/notification.go | 64 ++++++++++++++++++++---- pkg/cmd/notificationcheck.go | 15 ++++-- pkg/cmd/profile.go | 22 +++++++-- pkg/cmd/profilelist.go | 22 +++++++-- pkg/cmd/provider.go | 29 +++++++++-- pkg/cmd/providercatalog.go | 8 ++- pkg/cmd/routingstrategy.go | 36 +++++++++++--- pkg/cmd/send.go | 8 ++- pkg/cmd/tenant.go | 29 +++++++++-- pkg/cmd/tenanttemplate.go | 29 +++++++++-- pkg/cmd/tenanttemplateversion.go | 8 ++- pkg/cmd/translation.go | 8 ++- pkg/cmd/userpreference.go | 22 +++++++-- pkg/cmd/usertenant.go | 8 ++- pkg/cmd/usertoken.go | 15 ++++-- 30 files changed, 519 insertions(+), 134 deletions(-) diff --git a/cmd/courier/main.go b/cmd/courier/main.go index cc55f81..b0197a2 100644 --- a/cmd/courier/main.go +++ b/cmd/courier/main.go @@ -43,7 +43,12 @@ func main() { fmt.Fprintf(os.Stderr, "%s %q: %d %s\n", apierr.Request.Method, apierr.Request.URL, apierr.Response.StatusCode, http.StatusText(apierr.Response.StatusCode)) format := app.String("format-error") json := gjson.Parse(apierr.RawJSON()) - show_err := cmd.ShowJSON(os.Stdout, os.Stderr, "Error", json, format, app.IsSet("format-error"), app.String("transform-error")) + show_err := cmd.ShowJSON(json, cmd.ShowJSONOpts{ + ExplicitFormat: app.IsSet("format-error"), + Format: format, + Title: "Error", + Transform: app.String("transform-error"), + }) if show_err != nil { // Just print the original error: fmt.Fprintf(os.Stderr, "%s\n", err.Error()) diff --git a/pkg/cmd/audience.go b/pkg/cmd/audience.go index 1d41d96..2bb52a7 100644 --- a/pkg/cmd/audience.go +++ b/pkg/cmd/audience.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -152,7 +151,12 @@ func handleAudiencesRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "audiences retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "audiences retrieve", + Transform: transform, + }) } func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { @@ -195,7 +199,12 @@ func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "audiences update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "audiences update", + Transform: transform, + }) } func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { @@ -230,7 +239,12 @@ func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "audiences list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "audiences list", + Transform: transform, + }) } func handleAudiencesDelete(ctx context.Context, cmd *cli.Command) error { @@ -298,5 +312,10 @@ func handleAudiencesListMembers(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "audiences list-members", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "audiences list-members", + Transform: transform, + }) } diff --git a/pkg/cmd/auditevent.go b/pkg/cmd/auditevent.go index 4524694..643aa4d 100644 --- a/pkg/cmd/auditevent.go +++ b/pkg/cmd/auditevent.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -77,7 +76,12 @@ func handleAuditEventsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "audit-events retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "audit-events retrieve", + Transform: transform, + }) } func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { @@ -112,5 +116,10 @@ func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "audit-events list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "audit-events list", + Transform: transform, + }) } diff --git a/pkg/cmd/auth.go b/pkg/cmd/auth.go index 5a83913..4d4007b 100644 --- a/pkg/cmd/auth.go +++ b/pkg/cmd/auth.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -69,5 +68,10 @@ func handleAuthIssueToken(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "auth issue-token", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "auth issue-token", + Transform: transform, + }) } diff --git a/pkg/cmd/automation.go b/pkg/cmd/automation.go index bd670fb..75a46d4 100644 --- a/pkg/cmd/automation.go +++ b/pkg/cmd/automation.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -68,5 +67,10 @@ func handleAutomationsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "automations list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "automations list", + Transform: transform, + }) } diff --git a/pkg/cmd/automationinvoke.go b/pkg/cmd/automationinvoke.go index 2e65e9a..a3f22a9 100644 --- a/pkg/cmd/automationinvoke.go +++ b/pkg/cmd/automationinvoke.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -128,7 +127,12 @@ func handleAutomationsInvokeInvokeAdHoc(ctx context.Context, cmd *cli.Command) e format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "automations:invoke invoke-ad-hoc", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "automations:invoke invoke-ad-hoc", + Transform: transform, + }) } func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Command) error { @@ -171,5 +175,10 @@ func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Comma format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "automations:invoke invoke-by-template", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "automations:invoke invoke-by-template", + Transform: transform, + }) } diff --git a/pkg/cmd/brand.go b/pkg/cmd/brand.go index 9e237ac..1b17f8b 100644 --- a/pkg/cmd/brand.go +++ b/pkg/cmd/brand.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -187,7 +186,12 @@ func handleBrandsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "brands create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "brands create", + Transform: transform, + }) } func handleBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -223,7 +227,12 @@ func handleBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "brands retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "brands retrieve", + Transform: transform, + }) } func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -266,7 +275,12 @@ func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "brands update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "brands update", + Transform: transform, + }) } func handleBrandsList(ctx context.Context, cmd *cli.Command) error { @@ -301,7 +315,12 @@ func handleBrandsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "brands list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "brands list", + Transform: transform, + }) } func handleBrandsDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/bulk.go b/pkg/cmd/bulk.go index cfa0a5a..0cf003a 100644 --- a/pkg/cmd/bulk.go +++ b/pkg/cmd/bulk.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -221,7 +220,12 @@ func handleBulkCreateJob(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "bulk create-job", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "bulk create-job", + Transform: transform, + }) } func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { @@ -264,7 +268,12 @@ func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "bulk list-users", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "bulk list-users", + Transform: transform, + }) } func handleBulkRetrieveJob(ctx context.Context, cmd *cli.Command) error { @@ -300,7 +309,12 @@ func handleBulkRetrieveJob(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "bulk retrieve-job", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "bulk retrieve-job", + Transform: transform, + }) } func handleBulkRunJob(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 0eaacf4..878b130 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -356,36 +356,58 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format const warningExploreNotSupported = "Warning: Output format 'explore' not supported for non-terminal output; falling back to 'json'\n" -// Display JSON to the user in various different formats. The explicitFormat parameter indicates -// whether the format was explicitly set by the user (via --format), which controls whether we -// silently fall back to json when explore is requested on non-terminal output. Warnings are -// written to stderr. -func ShowJSON(out *os.File, stderr io.Writer, title string, res gjson.Result, format string, explicitFormat bool, transform string) error { - if transform != "" { - transformed := res.Get(transform) +// ShowJSONOpts configures how JSON output is displayed. +type ShowJSONOpts struct { + ExplicitFormat bool // true if the user explicitly passed --format + Format string // output format (auto, explore, json, jsonl, pretty, raw, yaml) + Stderr io.Writer // stderr for warnings; injectable for testing; defaults to os.Stderr + Stdout *os.File // stdout (or pager); injectable for testing; defaults to os.Stdout + Title string // display title + Transform string // GJSON path to extract before displaying +} + +func (o *ShowJSONOpts) setDefaults() { + if o.Stderr == nil { + o.Stderr = os.Stderr + } + if o.Stdout == nil { + o.Stdout = os.Stdout + } +} + +// ShowJSON displays a single JSON result to the user. +func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { + opts.setDefaults() + + if opts.Transform != "" { + transformed := res.Get(opts.Transform) if transformed.Exists() { res = transformed } } - switch strings.ToLower(format) { + switch strings.ToLower(opts.Format) { case "auto": - return ShowJSON(out, stderr, title, res, "json", explicitFormat, "") + jsonOpts := opts + jsonOpts.Format = "json" + return ShowJSON(res, jsonOpts) case "explore": - if !isTerminal(out) { - if explicitFormat { - fmt.Fprint(stderr, warningExploreNotSupported) + if !isTerminal(opts.Stdout) { + if opts.ExplicitFormat { + fmt.Fprint(opts.Stderr, warningExploreNotSupported) } - return ShowJSON(out, stderr, title, res, "json", explicitFormat, transform) + jsonOpts := opts + jsonOpts.Format = "json" + return ShowJSON(res, jsonOpts) } - return jsonview.ExploreJSON(title, res) + return jsonview.ExploreJSON(opts.Title, res) default: - bytes, err := formatJSON(out, title, res, format, transform) + bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform) if err != nil { return err } - _, err = out.Write(bytes) + _, err = opts.Stdout.Write(bytes) return err } } @@ -399,16 +421,17 @@ type hasRawJSON interface { RawJSON() string } -// For an iterator over different value types, display its values to the user in -// different formats. -// -1 is used to signal no limit of items to display -func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, iter jsonview.Iterator[T], format string, explicitFormat bool, transform string, itemsToDisplay int64) error { +// ShowJSONIterator displays an iterator of values to the user. Use itemsToDisplay = -1 for no limit. +func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, opts ShowJSONOpts) error { + opts.setDefaults() + + format := opts.Format if format == "explore" { - if isTerminal(stdout) { - return jsonview.ExploreJSONStream(title, iter) + if isTerminal(opts.Stdout) { + return jsonview.ExploreJSONStream(opts.Title, iter) } - if explicitFormat { - fmt.Fprint(stderr, warningExploreNotSupported) + if opts.ExplicitFormat { + fmt.Fprint(opts.Stderr, warningExploreNotSupported) } format = "json" } @@ -436,7 +459,7 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it } obj = gjson.ParseBytes(jsonData) } - json, err := formatJSON(stdout, title, obj, format, transform) + json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform) if err != nil { return err } @@ -453,7 +476,7 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it } if !usePager { - _, err := stdout.Write(output) + _, err := opts.Stdout.Write(output) if err != nil { return err } @@ -461,13 +484,16 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it return iter.Err() } - return streamOutput(title, func(pager *os.File) error { - // Write the output we used during the initial terminal size computation + return streamOutput(opts.Title, func(pager *os.File) error { _, err := pager.Write(output) if err != nil { return err } + pagerOpts := opts + pagerOpts.Format = format + pagerOpts.Stdout = pager + for iter.Next() { if itemsToDisplay == 0 { break @@ -483,7 +509,7 @@ func ShowJSONIterator[T any](stdout *os.File, stderr io.Writer, title string, it } obj = gjson.ParseBytes(jsonData) } - if err := ShowJSON(pager, stderr, title, obj, format, explicitFormat, transform); err != nil { + if err := ShowJSON(obj, pagerOpts); err != nil { return err } itemsToDisplay -= 1 diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 0dcbab0..ebcf3aa 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -244,7 +244,12 @@ func TestExploreFallback(t *testing.T) { var stderr bytes.Buffer res := gjson.Parse(`{"id":"abc"}`) - err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + err = ShowJSON(res, ShowJSONOpts{ + Format: "explore", + Stderr: &stderr, + Stdout: w, + Title: "test", + }) w.Close() require.NoError(t, err) @@ -274,7 +279,13 @@ func TestExploreFallback(t *testing.T) { var stderr bytes.Buffer res := gjson.Parse(`{"id":"abc"}`) - err = ShowJSON(w, &stderr, "test", res, "explore", true, "") + err = ShowJSON(res, ShowJSONOpts{ + ExplicitFormat: true, + Format: "explore", + Stderr: &stderr, + Stdout: w, + Title: "test", + }) w.Close() require.NoError(t, err) @@ -290,7 +301,12 @@ func TestExploreFallback(t *testing.T) { var stderr bytes.Buffer res := gjson.Parse(`{"id":"abc"}`) - err = ShowJSON(w, &stderr, "test", res, "explore", false, "") + err = ShowJSON(res, ShowJSONOpts{ + Format: "explore", + Stderr: &stderr, + Stdout: w, + Title: "test", + }) w.Close() require.NoError(t, err) @@ -327,8 +343,13 @@ func captureShowJSONIterator[T any](t *testing.T, iter jsonview.Iterator[T], for require.NoError(t, err) defer r.Close() - var stderr bytes.Buffer - err = ShowJSONIterator(w, &stderr, "test", iter, format, false, transform, itemsToDisplay) + err = ShowJSONIterator(iter, itemsToDisplay, ShowJSONOpts{ + Format: format, + Stderr: io.Discard, + Stdout: w, + Title: "test", + Transform: transform, + }) w.Close() require.NoError(t, err) diff --git a/pkg/cmd/inbound.go b/pkg/cmd/inbound.go index a120485..27440f2 100644 --- a/pkg/cmd/inbound.go +++ b/pkg/cmd/inbound.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -85,5 +84,10 @@ func handleInboundTrackEvent(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "inbound track-event", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "inbound track-event", + Transform: transform, + }) } diff --git a/pkg/cmd/journey.go b/pkg/cmd/journey.go index 22c54b1..3d9dfe9 100644 --- a/pkg/cmd/journey.go +++ b/pkg/cmd/journey.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -97,7 +96,12 @@ func handleJourneysList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "journeys list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "journeys list", + Transform: transform, + }) } func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { @@ -140,5 +144,10 @@ func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "journeys invoke", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "journeys invoke", + Transform: transform, + }) } diff --git a/pkg/cmd/list.go b/pkg/cmd/list.go index fd32c45..936dea1 100644 --- a/pkg/cmd/list.go +++ b/pkg/cmd/list.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -144,7 +143,12 @@ func handleListsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "lists retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "lists retrieve", + Transform: transform, + }) } func handleListsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -211,7 +215,12 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "lists list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "lists list", + Transform: transform, + }) } func handleListsDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/listsubscription.go b/pkg/cmd/listsubscription.go index 26f0da7..922c3ac 100644 --- a/pkg/cmd/listsubscription.go +++ b/pkg/cmd/listsubscription.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -185,7 +184,12 @@ func handleListsSubscriptionsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "lists:subscriptions list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "lists:subscriptions list", + Transform: transform, + }) } func handleListsSubscriptionsAdd(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index bf44193..8909afb 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -189,7 +188,12 @@ func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages retrieve", + Transform: transform, + }) } func handleMessagesList(ctx context.Context, cmd *cli.Command) error { @@ -224,7 +228,12 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages list", + Transform: transform, + }) } func handleMessagesCancel(ctx context.Context, cmd *cli.Command) error { @@ -260,7 +269,12 @@ func handleMessagesCancel(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages cancel", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages cancel", + Transform: transform, + }) } func handleMessagesContent(ctx context.Context, cmd *cli.Command) error { @@ -296,7 +310,12 @@ func handleMessagesContent(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages content", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages content", + Transform: transform, + }) } func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { @@ -339,5 +358,10 @@ func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "messages history", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "messages history", + Transform: transform, + }) } diff --git a/pkg/cmd/notification.go b/pkg/cmd/notification.go index 99e733f..53a88bf 100644 --- a/pkg/cmd/notification.go +++ b/pkg/cmd/notification.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -406,7 +405,12 @@ func handleNotificationsCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications create", + Transform: transform, + }) } func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -449,7 +453,12 @@ func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications retrieve", + Transform: transform, + }) } func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { @@ -484,7 +493,12 @@ func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications list", + Transform: transform, + }) } func handleNotificationsArchive(ctx context.Context, cmd *cli.Command) error { @@ -552,7 +566,12 @@ func handleNotificationsListVersions(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications list-versions", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications list-versions", + Transform: transform, + }) } func handleNotificationsPublish(ctx context.Context, cmd *cli.Command) error { @@ -627,7 +646,12 @@ func handleNotificationsPutContent(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications put-content", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications put-content", + Transform: transform, + }) } func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error { @@ -672,7 +696,12 @@ func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications put-element", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications put-element", + Transform: transform, + }) } func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { @@ -717,7 +746,12 @@ func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications put-locale", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications put-locale", + Transform: transform, + }) } func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { @@ -760,7 +794,12 @@ func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications replace", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications replace", + Transform: transform, + }) } func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) error { @@ -803,5 +842,10 @@ func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) e format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications retrieve-content", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications retrieve-content", + Transform: transform, + }) } diff --git a/pkg/cmd/notificationcheck.go b/pkg/cmd/notificationcheck.go index 4c3d841..084fc97 100644 --- a/pkg/cmd/notificationcheck.go +++ b/pkg/cmd/notificationcheck.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -133,7 +132,12 @@ func handleNotificationsChecksUpdate(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications:checks update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications:checks update", + Transform: transform, + }) } func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error { @@ -178,7 +182,12 @@ func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "notifications:checks list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "notifications:checks list", + Transform: transform, + }) } func handleNotificationsChecksDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/profile.go b/pkg/cmd/profile.go index cfdc9e9..5a63119 100644 --- a/pkg/cmd/profile.go +++ b/pkg/cmd/profile.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -159,7 +158,12 @@ func handleProfilesCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "profiles create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "profiles create", + Transform: transform, + }) } func handleProfilesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -195,7 +199,12 @@ func handleProfilesRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "profiles retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "profiles retrieve", + Transform: transform, + }) } func handleProfilesUpdate(ctx context.Context, cmd *cli.Command) error { @@ -295,5 +304,10 @@ func handleProfilesReplace(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "profiles replace", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "profiles replace", + Transform: transform, + }) } diff --git a/pkg/cmd/profilelist.go b/pkg/cmd/profilelist.go index 2ac2164..f56f7e5 100644 --- a/pkg/cmd/profilelist.go +++ b/pkg/cmd/profilelist.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -118,7 +117,12 @@ func handleProfilesListsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "profiles:lists retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "profiles:lists retrieve", + Transform: transform, + }) } func handleProfilesListsDelete(ctx context.Context, cmd *cli.Command) error { @@ -154,7 +158,12 @@ func handleProfilesListsDelete(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "profiles:lists delete", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "profiles:lists delete", + Transform: transform, + }) } func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { @@ -197,5 +206,10 @@ func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "profiles:lists subscribe", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "profiles:lists subscribe", + Transform: transform, + }) } diff --git a/pkg/cmd/provider.go b/pkg/cmd/provider.go index 87df70b..3ce3266 100644 --- a/pkg/cmd/provider.go +++ b/pkg/cmd/provider.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -156,7 +155,12 @@ func handleProvidersCreate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "providers create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "providers create", + Transform: transform, + }) } func handleProvidersRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -192,7 +196,12 @@ func handleProvidersRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "providers retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "providers retrieve", + Transform: transform, + }) } func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { @@ -235,7 +244,12 @@ func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "providers update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "providers update", + Transform: transform, + }) } func handleProvidersList(ctx context.Context, cmd *cli.Command) error { @@ -270,7 +284,12 @@ func handleProvidersList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "providers list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "providers list", + Transform: transform, + }) } func handleProvidersDelete(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/providercatalog.go b/pkg/cmd/providercatalog.go index e8b42fa..cc77a2c 100644 --- a/pkg/cmd/providercatalog.go +++ b/pkg/cmd/providercatalog.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -72,5 +71,10 @@ func handleProvidersCatalogList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "providers:catalog list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "providers:catalog list", + Transform: transform, + }) } diff --git a/pkg/cmd/routingstrategy.go b/pkg/cmd/routingstrategy.go index dbecbd8..1dbd71e 100644 --- a/pkg/cmd/routingstrategy.go +++ b/pkg/cmd/routingstrategy.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -227,7 +226,12 @@ func handleRoutingStrategiesCreate(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "routing-strategies create", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "routing-strategies create", + Transform: transform, + }) } func handleRoutingStrategiesRetrieve(ctx context.Context, cmd *cli.Command) error { @@ -263,7 +267,12 @@ func handleRoutingStrategiesRetrieve(ctx context.Context, cmd *cli.Command) erro format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "routing-strategies retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "routing-strategies retrieve", + Transform: transform, + }) } func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { @@ -298,7 +307,12 @@ func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "routing-strategies list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "routing-strategies list", + Transform: transform, + }) } func handleRoutingStrategiesArchive(ctx context.Context, cmd *cli.Command) error { @@ -366,7 +380,12 @@ func handleRoutingStrategiesListNotifications(ctx context.Context, cmd *cli.Comm format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "routing-strategies list-notifications", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "routing-strategies list-notifications", + Transform: transform, + }) } func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error { @@ -409,5 +428,10 @@ func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "routing-strategies replace", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "routing-strategies replace", + Transform: transform, + }) } diff --git a/pkg/cmd/send.go b/pkg/cmd/send.go index fe3117c..07df998 100644 --- a/pkg/cmd/send.go +++ b/pkg/cmd/send.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -125,5 +124,10 @@ func handleSendMessage(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "send message", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "send message", + Transform: transform, + }) } diff --git a/pkg/cmd/tenant.go b/pkg/cmd/tenant.go index 50cd6b1..e6b4f91 100644 --- a/pkg/cmd/tenant.go +++ b/pkg/cmd/tenant.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -176,7 +175,12 @@ func handleTenantsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants retrieve", + Transform: transform, + }) } func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { @@ -219,7 +223,12 @@ func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants update", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants update", + Transform: transform, + }) } func handleTenantsList(ctx context.Context, cmd *cli.Command) error { @@ -254,7 +263,12 @@ func handleTenantsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants list", + Transform: transform, + }) } func handleTenantsDelete(ctx context.Context, cmd *cli.Command) error { @@ -322,5 +336,10 @@ func handleTenantsListUsers(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants list-users", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants list-users", + Transform: transform, + }) } diff --git a/pkg/cmd/tenanttemplate.go b/pkg/cmd/tenanttemplate.go index 593930c..cf21501 100644 --- a/pkg/cmd/tenanttemplate.go +++ b/pkg/cmd/tenanttemplate.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -172,7 +171,12 @@ func handleTenantsTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants:templates retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants:templates retrieve", + Transform: transform, + }) } func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { @@ -215,7 +219,12 @@ func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants:templates list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants:templates list", + Transform: transform, + }) } func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error { @@ -260,7 +269,12 @@ func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants:templates publish", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants:templates publish", + Transform: transform, + }) } func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error { @@ -305,5 +319,10 @@ func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants:templates replace", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants:templates replace", + Transform: transform, + }) } diff --git a/pkg/cmd/tenanttemplateversion.go b/pkg/cmd/tenanttemplateversion.go index 90d23ed..3260045 100644 --- a/pkg/cmd/tenanttemplateversion.go +++ b/pkg/cmd/tenanttemplateversion.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -80,5 +79,10 @@ func handleTenantsTemplatesVersionsRetrieve(ctx context.Context, cmd *cli.Comman format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "tenants:templates:versions retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "tenants:templates:versions retrieve", + Transform: transform, + }) } diff --git a/pkg/cmd/translation.go b/pkg/cmd/translation.go index 1c65d74..2988d8a 100644 --- a/pkg/cmd/translation.go +++ b/pkg/cmd/translation.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -98,7 +97,12 @@ func handleTranslationsRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "translations retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "translations retrieve", + Transform: transform, + }) } func handleTranslationsUpdate(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/userpreference.go b/pkg/cmd/userpreference.go index 7a53d7f..6fcc56c 100644 --- a/pkg/cmd/userpreference.go +++ b/pkg/cmd/userpreference.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -142,7 +141,12 @@ func handleUsersPreferencesRetrieve(ctx context.Context, cmd *cli.Command) error format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "users:preferences retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "users:preferences retrieve", + Transform: transform, + }) } func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) error { @@ -187,7 +191,12 @@ func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "users:preferences retrieve-topic", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "users:preferences retrieve-topic", + Transform: transform, + }) } func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Command) error { @@ -232,5 +241,10 @@ func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Com format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "users:preferences update-or-create-topic", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "users:preferences update-or-create-topic", + Transform: transform, + }) } diff --git a/pkg/cmd/usertenant.go b/pkg/cmd/usertenant.go index 3528b1a..9fc5eb3 100644 --- a/pkg/cmd/usertenant.go +++ b/pkg/cmd/usertenant.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -175,7 +174,12 @@ func handleUsersTenantsList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "users:tenants list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "users:tenants list", + Transform: transform, + }) } func handleUsersTenantsAddMultiple(ctx context.Context, cmd *cli.Command) error { diff --git a/pkg/cmd/usertoken.go b/pkg/cmd/usertoken.go index 15ed67d..6933b08 100644 --- a/pkg/cmd/usertoken.go +++ b/pkg/cmd/usertoken.go @@ -5,7 +5,6 @@ package cmd import ( "context" "fmt" - "os" "github.com/tidwall/gjson" "github.com/trycourier/courier-cli/v3/internal/apiquery" @@ -261,7 +260,12 @@ func handleUsersTokensRetrieve(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "users:tokens retrieve", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "users:tokens retrieve", + Transform: transform, + }) } func handleUsersTokensUpdate(ctx context.Context, cmd *cli.Command) error { @@ -331,7 +335,12 @@ func handleUsersTokensList(ctx context.Context, cmd *cli.Command) error { format := cmd.Root().String("format") explicitFormat := cmd.Root().IsSet("format") transform := cmd.Root().String("transform") - return ShowJSON(os.Stdout, os.Stderr, "users:tokens list", obj, format, explicitFormat, transform) + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + Title: "users:tokens list", + Transform: transform, + }) } func handleUsersTokensDelete(ctx context.Context, cmd *cli.Command) error { From 99e6955357951c874d777c4a26a920d602b7ec72 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:48:06 +0000 Subject: [PATCH 05/29] chore(ci): support manually triggering release workflow --- .github/workflows/publish-release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index cfa2f18..86f6889 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -10,6 +10,7 @@ on: push: tags: - "v*" + workflow_dispatch: {} jobs: goreleaser: runs-on: ubuntu-latest From 8de28579f6a831bfdc78ec33b16bcf31c55d06bb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:49:47 +0000 Subject: [PATCH 06/29] feat(cli): send filename and content type when reading input from files --- pkg/cmd/flagoptions.go | 65 +++++++++++++++++++++++++++++++++++-- pkg/cmd/flagoptions_test.go | 59 ++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 5c70efe..7b63627 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -7,9 +7,11 @@ import ( "fmt" "io" "maps" + "mime" "mime/multipart" "net/http" "os" + "path/filepath" "reflect" "strings" "unicode/utf8" @@ -36,7 +38,14 @@ const ( type FileEmbedStyle int const ( + // EmbedText reads referenced files fully into memory and substitutes the file's contents back into the + // value as a string. Binary files are base64-encoded. Used for JSON request bodies and for headers and + // query parameters, where the file contents need to be serialized inline. EmbedText FileEmbedStyle = iota + + // EmbedIOReader replaces file references with an io.Reader that streams the file's contents. Used for + // `multipart/form-data` and `application/octet-stream` request bodies, where files are uploaded as binary + // parts rather than embedded into a text value. EmbedIOReader ) @@ -142,6 +151,20 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi if s == "" { return v, nil } + if embedStyle == EmbedIOReader { + if isStdinPath(s) { + r, err := stdin.read() + if err != nil { + return v, err + } + return reflect.ValueOf(io.NopCloser(r)), nil + } + upload, err := openFileUpload(s) + if err != nil { + return v, err + } + return reflect.ValueOf(upload), nil + } if isStdinPath(s) { content, err := stdin.readAll() if err != nil { @@ -250,7 +273,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi return reflect.ValueOf(io.NopCloser(r)), nil } - file, err := os.Open(filename) + upload, err := openFileUpload(filename) if err != nil { if !expectsFile { // For strings that start with "@" and don't look like a filename, return the string @@ -258,7 +281,7 @@ func embedFilesValue(v reflect.Value, embedStyle FileEmbedStyle, stdin *onceStdi } return v, err } - return reflect.ValueOf(file), nil + return reflect.ValueOf(upload), nil } } return v, nil @@ -492,6 +515,44 @@ func flagOptions( // as a file path without needing the "@" prefix. type FilePathValue string +// fileUpload wraps an io.Reader with filename and content-type metadata for +// use as a multipart form part. The apiform encoder detects the Filename and +// ContentType methods and uses them to populate the Content-Disposition +// filename and the Content-Type header on the part. +type fileUpload struct { + io.Reader // apiform checks for reader and reads its contents during encode + filename string + contentType string +} + +func (f fileUpload) Filename() string { return f.filename } +func (f fileUpload) ContentType() string { return f.contentType } +func (f fileUpload) Close() error { + if c, ok := f.Reader.(io.Closer); ok { + return c.Close() + } + return nil +} + +// openFileUpload opens the file at path and returns a fileUpload whose filename +// is the path's basename and whose content type is derived from the file +// extension (falling back to application/octet-stream when unknown). +func openFileUpload(path string) (fileUpload, error) { + file, err := os.Open(path) + if err != nil { + return fileUpload{}, err + } + contentType := mime.TypeByExtension(filepath.Ext(path)) + if contentType == "" { + contentType = "application/octet-stream" + } + return fileUpload{ + Reader: file, + filename: filepath.Base(path), + contentType: contentType, + }, nil +} + // applyDataAliases rewrites keys in a body map based on flag `DataAliases` metadata. For top-level flags, // `{alias: value}` becomes `{canonical: value}`. For inner flags (those registered under an outer flag // via WithInnerFlags), the alias translation is also applied to the nested map under the outer flag's diff --git a/pkg/cmd/flagoptions_test.go b/pkg/cmd/flagoptions_test.go index 039b9ff..00734ca 100644 --- a/pkg/cmd/flagoptions_test.go +++ b/pkg/cmd/flagoptions_test.go @@ -8,7 +8,6 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,7 +30,7 @@ func TestIsUTF8TextFile(t *testing.T) { } for _, tt := range tests { - assert.Equal(t, tt.expected, isUTF8TextFile(tt.content)) + require.Equal(t, tt.expected, isUTF8TextFile(tt.content)) } } @@ -226,10 +225,10 @@ func TestEmbedFiles(t *testing.T) { got, err := embedFiles(tt.input, EmbedText, nil) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { require.NoError(t, err) - assert.Equal(t, tt.want, got) + require.Equal(t, tt.want, got) } }) @@ -238,7 +237,7 @@ func TestEmbedFiles(t *testing.T) { _, err := embedFiles(tt.input, EmbedIOReader, nil) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { require.NoError(t, err) } @@ -333,6 +332,56 @@ func TestEmbedFilesStdin(t *testing.T) { }) } +// TestEmbedFilesUploadMetadata verifies that EmbedIOReader mode wraps file readers with filename and +// content-type metadata so the multipart encoder populates `Content-Disposition` and `Content-Type` headers. +func TestEmbedFilesUploadMetadata(t *testing.T) { + t.Parallel() + + tmpDir := t.TempDir() + writeTestFile(t, tmpDir, "hello.txt", "hi") + writeTestFile(t, tmpDir, "page.html", "") + writeTestFile(t, tmpDir, "blob.bin", "\x00\x01") + + cases := []struct { + basename string + wantContentType string + }{ + {"hello.txt", "text/plain; charset=utf-8"}, + {"page.html", "text/html; charset=utf-8"}, + {"blob.bin", "application/octet-stream"}, + } + + for _, tc := range cases { + t.Run("AtPrefix_"+tc.basename, func(t *testing.T) { + t.Parallel() + + path := filepath.Join(tmpDir, tc.basename) + withEmbedded, err := embedFiles(map[string]any{"file": "@" + path}, EmbedIOReader, nil) + require.NoError(t, err) + + upload, ok := withEmbedded.(map[string]any)["file"].(fileUpload) + require.True(t, ok, "expected fileUpload, got %T", withEmbedded.(map[string]any)["file"]) + require.Equal(t, tc.basename, upload.Filename()) + require.Equal(t, upload.ContentType(), tc.wantContentType) + require.NoError(t, upload.Close()) + }) + + t.Run("FilePathValue_"+tc.basename, func(t *testing.T) { + t.Parallel() + + path := filepath.Join(tmpDir, tc.basename) + withEmbedded, err := embedFiles(map[string]any{"file": FilePathValue(path)}, EmbedIOReader, nil) + require.NoError(t, err) + + upload, ok := withEmbedded.(map[string]any)["file"].(fileUpload) + require.True(t, ok, "expected fileUpload, got %T", withEmbedded.(map[string]any)["file"]) + require.Equal(t, tc.basename, upload.Filename()) + require.Equal(t, upload.ContentType(), tc.wantContentType) + require.NoError(t, upload.Close()) + }) + } +} + func writeTestFile(t *testing.T, dir, filename, content string) { t.Helper() From 780a9c0e5dc5f889e1756e00b7712b58fd1c2cf6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:51:33 +0000 Subject: [PATCH 07/29] feat(cli): add `--raw-output`/`-r` option to print raw (non-JSON) strings --- pkg/cmd/audience.go | 4 ++++ pkg/cmd/auditevent.go | 2 ++ pkg/cmd/auth.go | 1 + pkg/cmd/automation.go | 1 + pkg/cmd/automationinvoke.go | 2 ++ pkg/cmd/brand.go | 4 ++++ pkg/cmd/bulk.go | 3 +++ pkg/cmd/cmd.go | 5 +++++ pkg/cmd/cmdutil.go | 14 ++++++++---- pkg/cmd/cmdutil_test.go | 37 ++++++++++++++++++++++++++++---- pkg/cmd/inbound.go | 1 + pkg/cmd/journey.go | 2 ++ pkg/cmd/list.go | 2 ++ pkg/cmd/listsubscription.go | 1 + pkg/cmd/message.go | 5 +++++ pkg/cmd/notification.go | 9 ++++++++ pkg/cmd/notificationcheck.go | 2 ++ pkg/cmd/profile.go | 3 +++ pkg/cmd/profilelist.go | 3 +++ pkg/cmd/provider.go | 4 ++++ pkg/cmd/providercatalog.go | 1 + pkg/cmd/routingstrategy.go | 5 +++++ pkg/cmd/send.go | 1 + pkg/cmd/tenant.go | 4 ++++ pkg/cmd/tenanttemplate.go | 4 ++++ pkg/cmd/tenanttemplateversion.go | 1 + pkg/cmd/translation.go | 1 + pkg/cmd/userpreference.go | 3 +++ pkg/cmd/usertenant.go | 1 + pkg/cmd/usertoken.go | 2 ++ 30 files changed, 120 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/audience.go b/pkg/cmd/audience.go index 2bb52a7..1f3b207 100644 --- a/pkg/cmd/audience.go +++ b/pkg/cmd/audience.go @@ -154,6 +154,7 @@ func handleAudiencesRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "audiences retrieve", Transform: transform, }) @@ -202,6 +203,7 @@ func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "audiences update", Transform: transform, }) @@ -242,6 +244,7 @@ func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "audiences list", Transform: transform, }) @@ -315,6 +318,7 @@ func handleAudiencesListMembers(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "audiences list-members", Transform: transform, }) diff --git a/pkg/cmd/auditevent.go b/pkg/cmd/auditevent.go index 643aa4d..42068f3 100644 --- a/pkg/cmd/auditevent.go +++ b/pkg/cmd/auditevent.go @@ -79,6 +79,7 @@ func handleAuditEventsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "audit-events retrieve", Transform: transform, }) @@ -119,6 +120,7 @@ func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "audit-events list", Transform: transform, }) diff --git a/pkg/cmd/auth.go b/pkg/cmd/auth.go index 4d4007b..22845cc 100644 --- a/pkg/cmd/auth.go +++ b/pkg/cmd/auth.go @@ -71,6 +71,7 @@ func handleAuthIssueToken(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "auth issue-token", Transform: transform, }) diff --git a/pkg/cmd/automation.go b/pkg/cmd/automation.go index 75a46d4..7ea138b 100644 --- a/pkg/cmd/automation.go +++ b/pkg/cmd/automation.go @@ -70,6 +70,7 @@ func handleAutomationsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "automations list", Transform: transform, }) diff --git a/pkg/cmd/automationinvoke.go b/pkg/cmd/automationinvoke.go index a3f22a9..28389bb 100644 --- a/pkg/cmd/automationinvoke.go +++ b/pkg/cmd/automationinvoke.go @@ -130,6 +130,7 @@ func handleAutomationsInvokeInvokeAdHoc(ctx context.Context, cmd *cli.Command) e return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "automations:invoke invoke-ad-hoc", Transform: transform, }) @@ -178,6 +179,7 @@ func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Comma return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "automations:invoke invoke-by-template", Transform: transform, }) diff --git a/pkg/cmd/brand.go b/pkg/cmd/brand.go index 1b17f8b..c3bd96c 100644 --- a/pkg/cmd/brand.go +++ b/pkg/cmd/brand.go @@ -189,6 +189,7 @@ func handleBrandsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "brands create", Transform: transform, }) @@ -230,6 +231,7 @@ func handleBrandsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "brands retrieve", Transform: transform, }) @@ -278,6 +280,7 @@ func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "brands update", Transform: transform, }) @@ -318,6 +321,7 @@ func handleBrandsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "brands list", Transform: transform, }) diff --git a/pkg/cmd/bulk.go b/pkg/cmd/bulk.go index 0cf003a..a5f163a 100644 --- a/pkg/cmd/bulk.go +++ b/pkg/cmd/bulk.go @@ -223,6 +223,7 @@ func handleBulkCreateJob(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "bulk create-job", Transform: transform, }) @@ -271,6 +272,7 @@ func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "bulk list-users", Transform: transform, }) @@ -312,6 +314,7 @@ func handleBulkRetrieveJob(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "bulk retrieve-job", Transform: transform, }) diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index afe432e..bd4f45a 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -73,6 +73,11 @@ func init() { Name: "transform-error", Usage: "The GJSON transformation for errors.", }, + &cli.BoolFlag{ + Name: "raw-output", + Aliases: []string{"r"}, + Usage: "If the result is a string, print it without JSON quotes. This can be useful for making output transforms talk to non-JSON-based systems.", + }, &requestflag.Flag[string]{ Name: "api-key", Sources: cli.EnvVars("COURIER_API_KEY"), diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 878b130..bf6ba4a 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -311,16 +311,21 @@ func shouldUseColors(w io.Writer) bool { return isTerminal(w) } -func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string) ([]byte, error) { +func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string, rawOutput bool) ([]byte, error) { if transform != "" { transformed := res.Get(transform) if transformed.Exists() { res = transformed } } + // Modeled after `jq -r` (`--raw-output`): if the result is a string, print it without JSON quotes so that + // it's easier to pipe into other programs. + if rawOutput && res.Type == gjson.String { + return []byte(res.Str + "\n"), nil + } switch strings.ToLower(format) { case "auto": - return formatJSON(expectedOutput, title, res, "json", "") + return formatJSON(expectedOutput, title, res, "json", "", rawOutput) case "pretty": return []byte(jsonview.RenderJSON(title, res) + "\n"), nil case "json": @@ -360,6 +365,7 @@ const warningExploreNotSupported = "Warning: Output format 'explore' not support type ShowJSONOpts struct { ExplicitFormat bool // true if the user explicitly passed --format Format string // output format (auto, explore, json, jsonl, pretty, raw, yaml) + RawOutput bool // like jq -r: print strings without JSON quotes Stderr io.Writer // stderr for warnings; injectable for testing; defaults to os.Stderr Stdout *os.File // stdout (or pager); injectable for testing; defaults to os.Stdout Title string // display title @@ -402,7 +408,7 @@ func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { } return jsonview.ExploreJSON(opts.Title, res) default: - bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform) + bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform, opts.RawOutput) if err != nil { return err } @@ -459,7 +465,7 @@ func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, op } obj = gjson.ParseBytes(jsonData) } - json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform) + json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform, opts.RawOutput) if err != nil { return err } diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index ebcf3aa..614c48a 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -159,7 +159,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id", false) require.NoError(t, err) require.Equal(t, `"abc123"`+"\n", string(formatted)) }) @@ -168,7 +168,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "", false) require.NoError(t, err) require.Equal(t, `{"id":"abc123","name":"test"}`+"\n", string(formatted)) }) @@ -177,7 +177,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"data":{"items":[1,2,3]}}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items", false) require.NoError(t, err) require.Equal(t, "[1,2,3]\n", string(formatted)) }) @@ -186,11 +186,40 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing") + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing", false) require.NoError(t, err) // Transform path doesn't exist, so original result is returned require.Equal(t, `{"id":"abc123"}`+"\n", string(formatted)) }) + + t.Run("RawOutputString", func(t *testing.T) { + t.Parallel() + + res := gjson.Parse(`{"id":"abc123","name":"test"}`) + formatted, err := formatJSON(os.Stdout, "test", res, "json", "id", true) + require.NoError(t, err) + require.Equal(t, "abc123\n", string(formatted)) + }) + + t.Run("RawOutputNonString", func(t *testing.T) { + t.Parallel() + + // --raw-output has no effect on non-string values + res := gjson.Parse(`{"count":42}`) + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "count", true) + require.NoError(t, err) + require.Equal(t, "42\n", string(formatted)) + }) + + t.Run("RawOutputObject", func(t *testing.T) { + t.Parallel() + + // --raw-output has no effect on objects + res := gjson.Parse(`{"nested":{"a":1}}`) + formatted, err := formatJSON(os.Stdout, "test", res, "raw", "nested", true) + require.NoError(t, err) + require.Equal(t, `{"a":1}`+"\n", string(formatted)) + }) } func TestShowJSONIterator(t *testing.T) { diff --git a/pkg/cmd/inbound.go b/pkg/cmd/inbound.go index 27440f2..50600c9 100644 --- a/pkg/cmd/inbound.go +++ b/pkg/cmd/inbound.go @@ -87,6 +87,7 @@ func handleInboundTrackEvent(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "inbound track-event", Transform: transform, }) diff --git a/pkg/cmd/journey.go b/pkg/cmd/journey.go index 3d9dfe9..f371bf2 100644 --- a/pkg/cmd/journey.go +++ b/pkg/cmd/journey.go @@ -99,6 +99,7 @@ func handleJourneysList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "journeys list", Transform: transform, }) @@ -147,6 +148,7 @@ func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "journeys invoke", Transform: transform, }) diff --git a/pkg/cmd/list.go b/pkg/cmd/list.go index 936dea1..6ba3eab 100644 --- a/pkg/cmd/list.go +++ b/pkg/cmd/list.go @@ -146,6 +146,7 @@ func handleListsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "lists retrieve", Transform: transform, }) @@ -218,6 +219,7 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "lists list", Transform: transform, }) diff --git a/pkg/cmd/listsubscription.go b/pkg/cmd/listsubscription.go index 922c3ac..7f645c5 100644 --- a/pkg/cmd/listsubscription.go +++ b/pkg/cmd/listsubscription.go @@ -187,6 +187,7 @@ func handleListsSubscriptionsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "lists:subscriptions list", Transform: transform, }) diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index 8909afb..e478c52 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -191,6 +191,7 @@ func handleMessagesRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages retrieve", Transform: transform, }) @@ -231,6 +232,7 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages list", Transform: transform, }) @@ -272,6 +274,7 @@ func handleMessagesCancel(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages cancel", Transform: transform, }) @@ -313,6 +316,7 @@ func handleMessagesContent(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages content", Transform: transform, }) @@ -361,6 +365,7 @@ func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "messages history", Transform: transform, }) diff --git a/pkg/cmd/notification.go b/pkg/cmd/notification.go index 53a88bf..43ff6d5 100644 --- a/pkg/cmd/notification.go +++ b/pkg/cmd/notification.go @@ -408,6 +408,7 @@ func handleNotificationsCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications create", Transform: transform, }) @@ -456,6 +457,7 @@ func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications retrieve", Transform: transform, }) @@ -496,6 +498,7 @@ func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications list", Transform: transform, }) @@ -569,6 +572,7 @@ func handleNotificationsListVersions(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications list-versions", Transform: transform, }) @@ -649,6 +653,7 @@ func handleNotificationsPutContent(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications put-content", Transform: transform, }) @@ -699,6 +704,7 @@ func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications put-element", Transform: transform, }) @@ -749,6 +755,7 @@ func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications put-locale", Transform: transform, }) @@ -797,6 +804,7 @@ func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications replace", Transform: transform, }) @@ -845,6 +853,7 @@ func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) e return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications retrieve-content", Transform: transform, }) diff --git a/pkg/cmd/notificationcheck.go b/pkg/cmd/notificationcheck.go index 084fc97..03ab005 100644 --- a/pkg/cmd/notificationcheck.go +++ b/pkg/cmd/notificationcheck.go @@ -135,6 +135,7 @@ func handleNotificationsChecksUpdate(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications:checks update", Transform: transform, }) @@ -185,6 +186,7 @@ func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "notifications:checks list", Transform: transform, }) diff --git a/pkg/cmd/profile.go b/pkg/cmd/profile.go index 5a63119..3704fb8 100644 --- a/pkg/cmd/profile.go +++ b/pkg/cmd/profile.go @@ -161,6 +161,7 @@ func handleProfilesCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "profiles create", Transform: transform, }) @@ -202,6 +203,7 @@ func handleProfilesRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "profiles retrieve", Transform: transform, }) @@ -307,6 +309,7 @@ func handleProfilesReplace(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "profiles replace", Transform: transform, }) diff --git a/pkg/cmd/profilelist.go b/pkg/cmd/profilelist.go index f56f7e5..c6d3e76 100644 --- a/pkg/cmd/profilelist.go +++ b/pkg/cmd/profilelist.go @@ -120,6 +120,7 @@ func handleProfilesListsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "profiles:lists retrieve", Transform: transform, }) @@ -161,6 +162,7 @@ func handleProfilesListsDelete(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "profiles:lists delete", Transform: transform, }) @@ -209,6 +211,7 @@ func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "profiles:lists subscribe", Transform: transform, }) diff --git a/pkg/cmd/provider.go b/pkg/cmd/provider.go index 3ce3266..123c3dc 100644 --- a/pkg/cmd/provider.go +++ b/pkg/cmd/provider.go @@ -158,6 +158,7 @@ func handleProvidersCreate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "providers create", Transform: transform, }) @@ -199,6 +200,7 @@ func handleProvidersRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "providers retrieve", Transform: transform, }) @@ -247,6 +249,7 @@ func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "providers update", Transform: transform, }) @@ -287,6 +290,7 @@ func handleProvidersList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "providers list", Transform: transform, }) diff --git a/pkg/cmd/providercatalog.go b/pkg/cmd/providercatalog.go index cc77a2c..14ead71 100644 --- a/pkg/cmd/providercatalog.go +++ b/pkg/cmd/providercatalog.go @@ -74,6 +74,7 @@ func handleProvidersCatalogList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "providers:catalog list", Transform: transform, }) diff --git a/pkg/cmd/routingstrategy.go b/pkg/cmd/routingstrategy.go index 1dbd71e..5513892 100644 --- a/pkg/cmd/routingstrategy.go +++ b/pkg/cmd/routingstrategy.go @@ -229,6 +229,7 @@ func handleRoutingStrategiesCreate(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "routing-strategies create", Transform: transform, }) @@ -270,6 +271,7 @@ func handleRoutingStrategiesRetrieve(ctx context.Context, cmd *cli.Command) erro return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "routing-strategies retrieve", Transform: transform, }) @@ -310,6 +312,7 @@ func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "routing-strategies list", Transform: transform, }) @@ -383,6 +386,7 @@ func handleRoutingStrategiesListNotifications(ctx context.Context, cmd *cli.Comm return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "routing-strategies list-notifications", Transform: transform, }) @@ -431,6 +435,7 @@ func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "routing-strategies replace", Transform: transform, }) diff --git a/pkg/cmd/send.go b/pkg/cmd/send.go index 07df998..7d98643 100644 --- a/pkg/cmd/send.go +++ b/pkg/cmd/send.go @@ -127,6 +127,7 @@ func handleSendMessage(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "send message", Transform: transform, }) diff --git a/pkg/cmd/tenant.go b/pkg/cmd/tenant.go index e6b4f91..4b4a600 100644 --- a/pkg/cmd/tenant.go +++ b/pkg/cmd/tenant.go @@ -178,6 +178,7 @@ func handleTenantsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants retrieve", Transform: transform, }) @@ -226,6 +227,7 @@ func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants update", Transform: transform, }) @@ -266,6 +268,7 @@ func handleTenantsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants list", Transform: transform, }) @@ -339,6 +342,7 @@ func handleTenantsListUsers(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants list-users", Transform: transform, }) diff --git a/pkg/cmd/tenanttemplate.go b/pkg/cmd/tenanttemplate.go index cf21501..4ef793b 100644 --- a/pkg/cmd/tenanttemplate.go +++ b/pkg/cmd/tenanttemplate.go @@ -174,6 +174,7 @@ func handleTenantsTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants:templates retrieve", Transform: transform, }) @@ -222,6 +223,7 @@ func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants:templates list", Transform: transform, }) @@ -272,6 +274,7 @@ func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants:templates publish", Transform: transform, }) @@ -322,6 +325,7 @@ func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants:templates replace", Transform: transform, }) diff --git a/pkg/cmd/tenanttemplateversion.go b/pkg/cmd/tenanttemplateversion.go index 3260045..b65cbb1 100644 --- a/pkg/cmd/tenanttemplateversion.go +++ b/pkg/cmd/tenanttemplateversion.go @@ -82,6 +82,7 @@ func handleTenantsTemplatesVersionsRetrieve(ctx context.Context, cmd *cli.Comman return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "tenants:templates:versions retrieve", Transform: transform, }) diff --git a/pkg/cmd/translation.go b/pkg/cmd/translation.go index 2988d8a..70b34bd 100644 --- a/pkg/cmd/translation.go +++ b/pkg/cmd/translation.go @@ -100,6 +100,7 @@ func handleTranslationsRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "translations retrieve", Transform: transform, }) diff --git a/pkg/cmd/userpreference.go b/pkg/cmd/userpreference.go index 6fcc56c..bf711e8 100644 --- a/pkg/cmd/userpreference.go +++ b/pkg/cmd/userpreference.go @@ -144,6 +144,7 @@ func handleUsersPreferencesRetrieve(ctx context.Context, cmd *cli.Command) error return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "users:preferences retrieve", Transform: transform, }) @@ -194,6 +195,7 @@ func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "users:preferences retrieve-topic", Transform: transform, }) @@ -244,6 +246,7 @@ func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Com return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "users:preferences update-or-create-topic", Transform: transform, }) diff --git a/pkg/cmd/usertenant.go b/pkg/cmd/usertenant.go index 9fc5eb3..ab6c77a 100644 --- a/pkg/cmd/usertenant.go +++ b/pkg/cmd/usertenant.go @@ -177,6 +177,7 @@ func handleUsersTenantsList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "users:tenants list", Transform: transform, }) diff --git a/pkg/cmd/usertoken.go b/pkg/cmd/usertoken.go index 6933b08..4776d7a 100644 --- a/pkg/cmd/usertoken.go +++ b/pkg/cmd/usertoken.go @@ -263,6 +263,7 @@ func handleUsersTokensRetrieve(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "users:tokens retrieve", Transform: transform, }) @@ -338,6 +339,7 @@ func handleUsersTokensList(ctx context.Context, cmd *cli.Command) error { return ShowJSON(obj, ShowJSONOpts{ ExplicitFormat: explicitFormat, Format: format, + RawOutput: cmd.Root().Bool("raw-output"), Title: "users:tokens list", Transform: transform, }) From 747c7090e140eb34ff022c5d9e42454618efe5c4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 02:49:10 +0000 Subject: [PATCH 08/29] chore(cli): use `ShowJSONOpts` as argument to `formatJSON` instead of many positionals --- pkg/cmd/cmdutil.go | 54 ++++++++++++++++++++--------------------- pkg/cmd/cmdutil_test.go | 14 +++++------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index bf6ba4a..5aed463 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -311,26 +311,29 @@ func shouldUseColors(w io.Writer) bool { return isTerminal(w) } -func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format string, transform string, rawOutput bool) ([]byte, error) { - if transform != "" { - transformed := res.Get(transform) +func formatJSON(res gjson.Result, opts ShowJSONOpts) ([]byte, error) { + if opts.Transform != "" { + transformed := res.Get(opts.Transform) if transformed.Exists() { res = transformed } } // Modeled after `jq -r` (`--raw-output`): if the result is a string, print it without JSON quotes so that // it's easier to pipe into other programs. - if rawOutput && res.Type == gjson.String { + if opts.RawOutput && res.Type == gjson.String { return []byte(res.Str + "\n"), nil } - switch strings.ToLower(format) { + switch strings.ToLower(opts.Format) { case "auto": - return formatJSON(expectedOutput, title, res, "json", "", rawOutput) + autoOpts := opts + autoOpts.Format = "json" + autoOpts.Transform = "" + return formatJSON(res, autoOpts) case "pretty": - return []byte(jsonview.RenderJSON(title, res) + "\n"), nil + return []byte(jsonview.RenderJSON(opts.Title, res) + "\n"), nil case "json": prettyJSON := pretty.Pretty([]byte(res.Raw)) - if shouldUseColors(expectedOutput) { + if shouldUseColors(opts.Stdout) { return pretty.Color(prettyJSON, pretty.TerminalStyle), nil } else { return prettyJSON, nil @@ -338,7 +341,7 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format case "jsonl": // @ugly is gjson syntax for "no whitespace", so it fits on one line oneLineJSON := res.Get("@ugly").Raw - if shouldUseColors(expectedOutput) { + if shouldUseColors(opts.Stdout) { bytes := append(pretty.Color([]byte(oneLineJSON), pretty.TerminalStyle), '\n') return bytes, nil } else { @@ -352,10 +355,10 @@ func formatJSON(expectedOutput *os.File, title string, res gjson.Result, format if err := json2yaml.Convert(&yaml, input); err != nil { return nil, err } - _, err := expectedOutput.Write([]byte(yaml.String())) + _, err := opts.Stdout.Write([]byte(yaml.String())) return nil, err default: - return nil, fmt.Errorf("Invalid format: %s, valid formats are: %s", format, strings.Join(OutputFormats, ", ")) + return nil, fmt.Errorf("Invalid format: %s, valid formats are: %s", opts.Format, strings.Join(OutputFormats, ", ")) } } @@ -385,18 +388,11 @@ func (o *ShowJSONOpts) setDefaults() { func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { opts.setDefaults() - if opts.Transform != "" { - transformed := res.Get(opts.Transform) - if transformed.Exists() { - res = transformed - } - } - switch strings.ToLower(opts.Format) { case "auto": - jsonOpts := opts - jsonOpts.Format = "json" - return ShowJSON(res, jsonOpts) + autoOpts := opts + autoOpts.Format = "json" + return ShowJSON(res, autoOpts) case "explore": if !isTerminal(opts.Stdout) { if opts.ExplicitFormat { @@ -406,9 +402,15 @@ func ShowJSON(res gjson.Result, opts ShowJSONOpts) error { jsonOpts.Format = "json" return ShowJSON(res, jsonOpts) } + if opts.Transform != "" { + transformed := res.Get(opts.Transform) + if transformed.Exists() { + res = transformed + } + } return jsonview.ExploreJSON(opts.Title, res) default: - bytes, err := formatJSON(opts.Stdout, opts.Title, res, opts.Format, opts.Transform, opts.RawOutput) + bytes, err := formatJSON(res, opts) if err != nil { return err } @@ -431,15 +433,14 @@ type hasRawJSON interface { func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, opts ShowJSONOpts) error { opts.setDefaults() - format := opts.Format - if format == "explore" { + if opts.Format == "explore" { if isTerminal(opts.Stdout) { return jsonview.ExploreJSONStream(opts.Title, iter) } if opts.ExplicitFormat { fmt.Fprint(opts.Stderr, warningExploreNotSupported) } - format = "json" + opts.Format = "json" } terminalWidth, terminalHeight, err := term.GetSize(os.Stdout.Fd()) @@ -465,7 +466,7 @@ func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, op } obj = gjson.ParseBytes(jsonData) } - json, err := formatJSON(opts.Stdout, opts.Title, obj, format, opts.Transform, opts.RawOutput) + json, err := formatJSON(obj, opts) if err != nil { return err } @@ -497,7 +498,6 @@ func ShowJSONIterator[T any](iter jsonview.Iterator[T], itemsToDisplay int64, op } pagerOpts := opts - pagerOpts.Format = format pagerOpts.Stdout = pager for iter.Next() { diff --git a/pkg/cmd/cmdutil_test.go b/pkg/cmd/cmdutil_test.go index 614c48a..dca40be 100644 --- a/pkg/cmd/cmdutil_test.go +++ b/pkg/cmd/cmdutil_test.go @@ -159,7 +159,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "id", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "id"}) require.NoError(t, err) require.Equal(t, `"abc123"`+"\n", string(formatted)) }) @@ -168,7 +168,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout}) require.NoError(t, err) require.Equal(t, `{"id":"abc123","name":"test"}`+"\n", string(formatted)) }) @@ -177,7 +177,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"data":{"items":[1,2,3]}}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "data.items", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "data.items"}) require.NoError(t, err) require.Equal(t, "[1,2,3]\n", string(formatted)) }) @@ -186,7 +186,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "missing", false) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "missing"}) require.NoError(t, err) // Transform path doesn't exist, so original result is returned require.Equal(t, `{"id":"abc123"}`+"\n", string(formatted)) @@ -196,7 +196,7 @@ func TestFormatJSON(t *testing.T) { t.Parallel() res := gjson.Parse(`{"id":"abc123","name":"test"}`) - formatted, err := formatJSON(os.Stdout, "test", res, "json", "id", true) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "json", Stdout: os.Stdout, Transform: "id", RawOutput: true}) require.NoError(t, err) require.Equal(t, "abc123\n", string(formatted)) }) @@ -206,7 +206,7 @@ func TestFormatJSON(t *testing.T) { // --raw-output has no effect on non-string values res := gjson.Parse(`{"count":42}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "count", true) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "count", RawOutput: true}) require.NoError(t, err) require.Equal(t, "42\n", string(formatted)) }) @@ -216,7 +216,7 @@ func TestFormatJSON(t *testing.T) { // --raw-output has no effect on objects res := gjson.Parse(`{"nested":{"a":1}}`) - formatted, err := formatJSON(os.Stdout, "test", res, "raw", "nested", true) + formatted, err := formatJSON(res, ShowJSONOpts{Format: "raw", Stdout: os.Stdout, Transform: "nested", RawOutput: true}) require.NoError(t, err) require.Equal(t, `{"a":1}`+"\n", string(formatted)) }) From 39342478242318700e674e0ed42b1950e7bce5a1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 02:17:53 +0000 Subject: [PATCH 09/29] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index 9ebb7d3..bbc786d 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response From 8f3872f856ca43e04183c8f965f8e0e7ec1e0177 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 02:22:06 +0000 Subject: [PATCH 10/29] chore(internal): codegen related update --- .github/workflows/ci.yml | 8 ++++---- pkg/cmd/cmdutil.go | 2 +- pkg/cmd/flagoptions.go | 2 +- scripts/build | 2 +- scripts/link | 4 ++-- scripts/lint | 2 +- scripts/run | 2 +- scripts/test | 2 +- scripts/unlink | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1aa995..9bfd819 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ on: - 'stl-preview-base/**' env: - GOPRIVATE: github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4 + GOPRIVATE: github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go jobs: lint: @@ -34,7 +34,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -60,7 +60,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -107,7 +107,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 5aed463..158cc32 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -17,7 +17,7 @@ import ( "syscall" "github.com/trycourier/courier-cli/v3/internal/jsonview" - "github.com/trycourier/courier-go/v4/option" + "github.com/trycourier/courier-go/option" "github.com/charmbracelet/x/term" "github.com/itchyny/json2yaml" diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 7b63627..791c347 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -20,7 +20,7 @@ import ( "github.com/trycourier/courier-cli/v3/internal/apiquery" "github.com/trycourier/courier-cli/v3/internal/debugmiddleware" "github.com/trycourier/courier-cli/v3/internal/requestflag" - "github.com/trycourier/courier-go/v4/option" + "github.com/trycourier/courier-go/option" "github.com/goccy/go-yaml" "github.com/urfave/cli/v3" diff --git a/scripts/build b/scripts/build index 382cfac..c32066a 100755 --- a/scripts/build +++ b/scripts/build @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" echo "==> Building courier" go build ./cmd/courier diff --git a/scripts/link b/scripts/link index 6f8f111..a3e5f2a 100755 --- a/scripts/link +++ b/scripts/link @@ -5,12 +5,12 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" REPLACEMENT="${1:-"../Courier-go"}" echo "==> Replacing Go SDK with $REPLACEMENT" if [[ -d "$REPLACEMENT" ]] || go list -m "$REPLACEMENT" >/dev/null; then - go mod edit -replace github.com/trycourier/courier-go/v4="$REPLACEMENT" + go mod edit -replace github.com/trycourier/courier-go="$REPLACEMENT" go mod tidy -e else echo "Skipping Go SDK replacement (branch may not exist on Go SDK)" diff --git a/scripts/lint b/scripts/lint index d7aa3bd..738b28a 100755 --- a/scripts/lint +++ b/scripts/lint @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" echo "==> Running Go build" go build ./... diff --git a/scripts/run b/scripts/run index eb7c853..0ce2cf2 100755 --- a/scripts/run +++ b/scripts/run @@ -5,6 +5,6 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" go run ./cmd/courier "$@" diff --git a/scripts/test b/scripts/test index 67b365a..98c106b 100755 --- a/scripts/test +++ b/scripts/test @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" diff --git a/scripts/unlink b/scripts/unlink index a08fd99..b0fcc65 100755 --- a/scripts/unlink +++ b/scripts/unlink @@ -5,4 +5,4 @@ set -e cd "$(dirname "$0")/.." echo "==> Unlinking with local directory" -go mod edit -dropreplace github.com/trycourier/courier-go/v4 +go mod edit -dropreplace github.com/trycourier/courier-go From 0f66682369f79620f487884c535e41ad96304d95 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 02:22:54 +0000 Subject: [PATCH 11/29] chore(internal): codegen related update --- .github/workflows/ci.yml | 8 ++++---- pkg/cmd/cmdutil.go | 2 +- pkg/cmd/flagoptions.go | 2 +- scripts/build | 2 +- scripts/link | 4 ++-- scripts/lint | 2 +- scripts/run | 2 +- scripts/test | 2 +- scripts/unlink | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bfd819..f1aa995 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ on: - 'stl-preview-base/**' env: - GOPRIVATE: github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go + GOPRIVATE: github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4 jobs: lint: @@ -34,7 +34,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -60,7 +60,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -107,7 +107,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 158cc32..5aed463 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -17,7 +17,7 @@ import ( "syscall" "github.com/trycourier/courier-cli/v3/internal/jsonview" - "github.com/trycourier/courier-go/option" + "github.com/trycourier/courier-go/v4/option" "github.com/charmbracelet/x/term" "github.com/itchyny/json2yaml" diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 791c347..7b63627 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -20,7 +20,7 @@ import ( "github.com/trycourier/courier-cli/v3/internal/apiquery" "github.com/trycourier/courier-cli/v3/internal/debugmiddleware" "github.com/trycourier/courier-cli/v3/internal/requestflag" - "github.com/trycourier/courier-go/option" + "github.com/trycourier/courier-go/v4/option" "github.com/goccy/go-yaml" "github.com/urfave/cli/v3" diff --git a/scripts/build b/scripts/build index c32066a..382cfac 100755 --- a/scripts/build +++ b/scripts/build @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" echo "==> Building courier" go build ./cmd/courier diff --git a/scripts/link b/scripts/link index a3e5f2a..6f8f111 100755 --- a/scripts/link +++ b/scripts/link @@ -5,12 +5,12 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" REPLACEMENT="${1:-"../Courier-go"}" echo "==> Replacing Go SDK with $REPLACEMENT" if [[ -d "$REPLACEMENT" ]] || go list -m "$REPLACEMENT" >/dev/null; then - go mod edit -replace github.com/trycourier/courier-go="$REPLACEMENT" + go mod edit -replace github.com/trycourier/courier-go/v4="$REPLACEMENT" go mod tidy -e else echo "Skipping Go SDK replacement (branch may not exist on Go SDK)" diff --git a/scripts/lint b/scripts/lint index 738b28a..d7aa3bd 100755 --- a/scripts/lint +++ b/scripts/lint @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" echo "==> Running Go build" go build ./... diff --git a/scripts/run b/scripts/run index 0ce2cf2..eb7c853 100755 --- a/scripts/run +++ b/scripts/run @@ -5,6 +5,6 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" go run ./cmd/courier "$@" diff --git a/scripts/test b/scripts/test index 98c106b..67b365a 100755 --- a/scripts/test +++ b/scripts/test @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" diff --git a/scripts/unlink b/scripts/unlink index b0fcc65..a08fd99 100755 --- a/scripts/unlink +++ b/scripts/unlink @@ -5,4 +5,4 @@ set -e cd "$(dirname "$0")/.." echo "==> Unlinking with local directory" -go mod edit -dropreplace github.com/trycourier/courier-go +go mod edit -dropreplace github.com/trycourier/courier-go/v4 From cd851acc33bb7aa94374078ce84ca0e69064eec3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:45:42 +0000 Subject: [PATCH 12/29] chore(internal): codegen related update --- .github/workflows/ci.yml | 8 ++++---- pkg/cmd/cmdutil.go | 2 +- pkg/cmd/flagoptions.go | 2 +- scripts/build | 2 +- scripts/link | 4 ++-- scripts/lint | 2 +- scripts/run | 2 +- scripts/test | 2 +- scripts/unlink | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1aa995..9bfd819 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ on: - 'stl-preview-base/**' env: - GOPRIVATE: github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4 + GOPRIVATE: github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go jobs: lint: @@ -34,7 +34,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -60,7 +60,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -107,7 +107,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 5aed463..158cc32 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -17,7 +17,7 @@ import ( "syscall" "github.com/trycourier/courier-cli/v3/internal/jsonview" - "github.com/trycourier/courier-go/v4/option" + "github.com/trycourier/courier-go/option" "github.com/charmbracelet/x/term" "github.com/itchyny/json2yaml" diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 7b63627..791c347 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -20,7 +20,7 @@ import ( "github.com/trycourier/courier-cli/v3/internal/apiquery" "github.com/trycourier/courier-cli/v3/internal/debugmiddleware" "github.com/trycourier/courier-cli/v3/internal/requestflag" - "github.com/trycourier/courier-go/v4/option" + "github.com/trycourier/courier-go/option" "github.com/goccy/go-yaml" "github.com/urfave/cli/v3" diff --git a/scripts/build b/scripts/build index 382cfac..c32066a 100755 --- a/scripts/build +++ b/scripts/build @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" echo "==> Building courier" go build ./cmd/courier diff --git a/scripts/link b/scripts/link index 6f8f111..a3e5f2a 100755 --- a/scripts/link +++ b/scripts/link @@ -5,12 +5,12 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" REPLACEMENT="${1:-"../Courier-go"}" echo "==> Replacing Go SDK with $REPLACEMENT" if [[ -d "$REPLACEMENT" ]] || go list -m "$REPLACEMENT" >/dev/null; then - go mod edit -replace github.com/trycourier/courier-go/v4="$REPLACEMENT" + go mod edit -replace github.com/trycourier/courier-go="$REPLACEMENT" go mod tidy -e else echo "Skipping Go SDK replacement (branch may not exist on Go SDK)" diff --git a/scripts/lint b/scripts/lint index d7aa3bd..738b28a 100755 --- a/scripts/lint +++ b/scripts/lint @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" echo "==> Running Go build" go build ./... diff --git a/scripts/run b/scripts/run index eb7c853..0ce2cf2 100755 --- a/scripts/run +++ b/scripts/run @@ -5,6 +5,6 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" go run ./cmd/courier "$@" diff --git a/scripts/test b/scripts/test index 67b365a..98c106b 100755 --- a/scripts/test +++ b/scripts/test @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" diff --git a/scripts/unlink b/scripts/unlink index a08fd99..b0fcc65 100755 --- a/scripts/unlink +++ b/scripts/unlink @@ -5,4 +5,4 @@ set -e cd "$(dirname "$0")/.." echo "==> Unlinking with local directory" -go mod edit -dropreplace github.com/trycourier/courier-go/v4 +go mod edit -dropreplace github.com/trycourier/courier-go From b152ea0392fc2ee9857dde8affe7746cc07fa0cd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:15:35 +0000 Subject: [PATCH 13/29] chore(internal): codegen related update --- .github/workflows/ci.yml | 8 ++++---- pkg/cmd/cmdutil.go | 2 +- pkg/cmd/flagoptions.go | 2 +- scripts/build | 2 +- scripts/link | 4 ++-- scripts/lint | 2 +- scripts/run | 2 +- scripts/test | 2 +- scripts/unlink | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bfd819..f1aa995 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ on: - 'stl-preview-base/**' env: - GOPRIVATE: github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go + GOPRIVATE: github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4 jobs: lint: @@ -34,7 +34,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -60,7 +60,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap @@ -107,7 +107,7 @@ jobs: - name: Link staging branch if: github.repository == 'stainless-sdks/courier-cli' run: | - ./scripts/link 'github.com/stainless-sdks/courier-go@${{ github.ref_name }}' || true + ./scripts/link 'github.com/stainless-sdks/courier-go/v4@${{ github.ref_name }}' || true - name: Bootstrap run: ./scripts/bootstrap diff --git a/pkg/cmd/cmdutil.go b/pkg/cmd/cmdutil.go index 158cc32..5aed463 100644 --- a/pkg/cmd/cmdutil.go +++ b/pkg/cmd/cmdutil.go @@ -17,7 +17,7 @@ import ( "syscall" "github.com/trycourier/courier-cli/v3/internal/jsonview" - "github.com/trycourier/courier-go/option" + "github.com/trycourier/courier-go/v4/option" "github.com/charmbracelet/x/term" "github.com/itchyny/json2yaml" diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 791c347..7b63627 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -20,7 +20,7 @@ import ( "github.com/trycourier/courier-cli/v3/internal/apiquery" "github.com/trycourier/courier-cli/v3/internal/debugmiddleware" "github.com/trycourier/courier-cli/v3/internal/requestflag" - "github.com/trycourier/courier-go/option" + "github.com/trycourier/courier-go/v4/option" "github.com/goccy/go-yaml" "github.com/urfave/cli/v3" diff --git a/scripts/build b/scripts/build index c32066a..382cfac 100755 --- a/scripts/build +++ b/scripts/build @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" echo "==> Building courier" go build ./cmd/courier diff --git a/scripts/link b/scripts/link index a3e5f2a..6f8f111 100755 --- a/scripts/link +++ b/scripts/link @@ -5,12 +5,12 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" REPLACEMENT="${1:-"../Courier-go"}" echo "==> Replacing Go SDK with $REPLACEMENT" if [[ -d "$REPLACEMENT" ]] || go list -m "$REPLACEMENT" >/dev/null; then - go mod edit -replace github.com/trycourier/courier-go="$REPLACEMENT" + go mod edit -replace github.com/trycourier/courier-go/v4="$REPLACEMENT" go mod tidy -e else echo "Skipping Go SDK replacement (branch may not exist on Go SDK)" diff --git a/scripts/lint b/scripts/lint index 738b28a..d7aa3bd 100755 --- a/scripts/lint +++ b/scripts/lint @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" echo "==> Running Go build" go build ./... diff --git a/scripts/run b/scripts/run index 0ce2cf2..eb7c853 100755 --- a/scripts/run +++ b/scripts/run @@ -5,6 +5,6 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" go run ./cmd/courier "$@" diff --git a/scripts/test b/scripts/test index 98c106b..67b365a 100755 --- a/scripts/test +++ b/scripts/test @@ -5,7 +5,7 @@ set -euo pipefail cd "$(dirname "$0")/.." # Mark the necessary Go modules as private to avoid Go's proxy -export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go,github.com/stainless-sdks/courier-go" +export GOPRIVATE="${GOPRIVATE:+$GOPRIVATE,}github.com/trycourier/courier-go/v4,github.com/stainless-sdks/courier-go/v4" diff --git a/scripts/unlink b/scripts/unlink index b0fcc65..a08fd99 100755 --- a/scripts/unlink +++ b/scripts/unlink @@ -5,4 +5,4 @@ set -e cd "$(dirname "$0")/.." echo "==> Unlinking with local directory" -go mod edit -dropreplace github.com/trycourier/courier-go +go mod edit -dropreplace github.com/trycourier/courier-go/v4 From c58ffcd91a7c0fb5255b638a34a61c2c8146759c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:16:56 +0000 Subject: [PATCH 14/29] fix(cli): correctly load zsh autocompletion --- .../autocomplete/shellscripts/zsh_autocomplete.zsh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/internal/autocomplete/shellscripts/zsh_autocomplete.zsh b/internal/autocomplete/shellscripts/zsh_autocomplete.zsh index 4d4bdcd..d937171 100644 --- a/internal/autocomplete/shellscripts/zsh_autocomplete.zsh +++ b/internal/autocomplete/shellscripts/zsh_autocomplete.zsh @@ -1,5 +1,4 @@ -#!/bin/zsh -compdef ____APPNAME___zsh_autocomplete __APPNAME__ +#compdef __APPNAME__ ____APPNAME___zsh_autocomplete() { @@ -44,3 +43,14 @@ ____APPNAME___zsh_autocomplete() { ;; esac } + +# When installed in fpath (e.g., via Homebrew's zsh_completion stanza), this file +# is autoloaded as the function ___APPNAME__ and its body becomes that function's +# body. Detect that case via funcstack and dispatch to the completion function. +# When sourced (e.g., `source <(__APPNAME__ @completion zsh)`), register the +# function with compdef instead. +if [[ "${funcstack[1]}" = "___APPNAME__" ]]; then + ____APPNAME___zsh_autocomplete "$@" +else + compdef ____APPNAME___zsh_autocomplete __APPNAME__ +fi From 440363a4d3e48ed081abb1e78406002a31382af8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:19:22 +0000 Subject: [PATCH 15/29] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index effa635..7149859 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-3f78581b4e078a1f620d9f587f18d77bcde6d20f56b0e4ae798648f4236494fb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-3f78581b4e078a1f620d9f587f18d77bcde6d20f56b0e4ae798648f4236494fb.yml openapi_spec_hash: 6bd33e0396d85e11bb46f0d549af93a3 config_hash: afcc4f6f8c33ca3f338589e32e086f56 From 41b809656f1b5a1d4ba73b91e9c60533f032e5f0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:20:42 +0000 Subject: [PATCH 16/29] fix: flags for nullable body scalar fields are strictly typed --- internal/requestflag/innerflag.go | 16 +- internal/requestflag/requestflag.go | 128 ++++++++++++- internal/requestflag/requestflag_test.go | 234 +++++++++++++++++++++++ pkg/cmd/audience.go | 10 +- pkg/cmd/auditevent.go | 2 +- pkg/cmd/automationinvoke.go | 14 +- pkg/cmd/brand.go | 4 +- pkg/cmd/bulk.go | 10 +- pkg/cmd/inbound.go | 2 +- pkg/cmd/list.go | 4 +- pkg/cmd/listsubscription.go | 2 +- pkg/cmd/message.go | 30 +-- pkg/cmd/notification.go | 4 +- pkg/cmd/profilelist.go | 2 +- pkg/cmd/routingstrategy.go | 8 +- pkg/cmd/send.go | 4 +- pkg/cmd/tenant.go | 14 +- pkg/cmd/tenantpreferenceitem.go | 2 +- pkg/cmd/tenanttemplate.go | 4 +- pkg/cmd/userpreference.go | 8 +- pkg/cmd/usertenant.go | 8 +- pkg/cmd/usertoken.go | 22 +-- 22 files changed, 451 insertions(+), 81 deletions(-) diff --git a/internal/requestflag/innerflag.go b/internal/requestflag/innerflag.go index eeeb8bc..528915f 100644 --- a/internal/requestflag/innerflag.go +++ b/internal/requestflag/innerflag.go @@ -14,7 +14,8 @@ import ( type InnerFlag[ T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string | []float64 | []int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue | - string | float64 | int64 | bool, + string | float64 | int64 | bool | + *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue, ] struct { Name string // name of the flag DefaultText string // default text of the flag for usage purposes @@ -25,6 +26,12 @@ type InnerFlag[ OuterFlag cli.Flag // The flag on which this inner flag will set values InnerField string // The inner field which this flag will set DataAliases []string // alternate names recognized in YAML values passed as the outer flag + + // OuterIsArrayOfObjects tells an untyped outer flag (Flag[any], used for nullable + // complex schemas) to seed its underlying value as []map[string]any rather than + // map[string]any before SetInnerField runs. The hint is ignored for typed outer + // flags whose zero value already carries a dispatchable reflect.Kind. + OuterIsArrayOfObjects bool } // GetDataAliases returns the aliases recognized when parsing inner field keys from piped or flag YAML. @@ -76,6 +83,10 @@ func (f *InnerFlag[T]) Set(name string, rawVal string) error { } } + if seeder, ok := f.OuterFlag.(InnerFieldSeeder); ok { + seeder.SeedInnerCollection(f.OuterIsArrayOfObjects) + } + if settableInnerField, ok := f.OuterFlag.(SettableInnerField); ok { settableInnerField.SetInnerField(f.InnerField, parsedValue) } else { @@ -136,6 +147,9 @@ func (f *InnerFlag[T]) TypeName() string { if ty == nil { return "" } + if ty.Kind() == reflect.Pointer { + ty = ty.Elem() + } // Get base type name with special handling for built-in types getTypeName := func(t reflect.Type) string { diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go index bfaf064..54c2509 100644 --- a/internal/requestflag/requestflag.go +++ b/internal/requestflag/requestflag.go @@ -15,10 +15,15 @@ import ( // Flag [T] is a generic flag base which can be used to implement the most // common interfaces used by urfave/cli. Additionally, it allows specifying // where in an HTTP request the flag values should be placed (e.g. query, body, etc.). +// +// Pointer-to-primitive type parameters (e.g. *string) are used for flags whose underlying +// schema is nullable. They give flags a tri-state: unset (excluded from the request), +// set to the literal "null" (nil pointer → JSON null), or set to a value (*v → JSON value). type Flag[ T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string | []float64 | []int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue | - string | float64 | int64 | bool, + string | float64 | int64 | bool | + *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue, ] struct { Name string // name of the flag Category string // category of the flag, if any @@ -341,6 +346,11 @@ func (f *Flag[T]) TypeName() string { if ty == nil { return "" } + // Deref pointer-typed flags so --help surfaces the pointee kind (e.g. "string"), not + // Go's pointer syntax. + if ty.Kind() == reflect.Pointer { + ty = ty.Elem() + } // Get base type name with special handling for built-in types getTypeName := func(t reflect.Type) string { @@ -396,6 +406,8 @@ func (f *Flag[T]) IsMultiValueFlag() bool { } func (f *Flag[T]) IsBoolFlag() bool { + // Flag[*bool] is deliberately not treated as a bool flag — the pointer form needs an + // explicit value (`--foo true`, `--foo null`) to disambiguate the tri-state. _, isBool := any(f.Default).(bool) return isBool } @@ -419,7 +431,8 @@ func (f Flag[T]) IsLocal() bool { type cliValue[ T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string | []float64 | []int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue | string | - float64 | int64 | bool, + float64 | int64 | bool | + *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue, ] struct { value T } @@ -429,12 +442,27 @@ type cliValue[ func parseCLIArg[ T []any | []map[string]any | []DateTimeValue | []DateValue | []TimeValue | []string | []float64 | []int64 | []bool | any | map[string]any | DateTimeValue | DateValue | TimeValue | string | - float64 | int64 | bool, + float64 | int64 | bool | + *string | *float64 | *int64 | *bool | *DateTimeValue | *DateValue | *TimeValue, ](value string) (T, error) { var parsedValue any var err error var empty T + + if value == "null" { + switch any(empty).(type) { + // Pointer-to-primitive: explicit nil gives the tri-state its "null" state + // (unset / null / value). Without this, numeric flags would fail to parse + // "null" and string flags would accept the literal word as a raw value. + case *string, *int64, *float64, *bool, *DateValue, *DateTimeValue, *TimeValue: + return empty, nil + // Maps marshal nil as JSON null natively; short-circuit avoids a YAML round-trip. + case map[string]any: + return empty, nil + } + } + switch any(empty).(type) { case string: parsedValue = value @@ -465,6 +493,48 @@ func parseCLIArg[ parsedValue = t } + // Pointer-to-primitive flags reach here only when `value != "null"`; we parse the + // pointee type and return its address so JSON marshaling emits the underlying value. + case *string: + v := value + parsedValue = &v + case *int64: + var v int64 + v, err = strconv.ParseInt(value, 0, 64) + if err == nil { + parsedValue = &v + } + case *float64: + var v float64 + v, err = strconv.ParseFloat(value, 64) + if err == nil { + parsedValue = &v + } + case *bool: + var v bool + v, err = strconv.ParseBool(value) + if err == nil { + parsedValue = &v + } + case *DateTimeValue: + var dt DateTimeValue + err = (&dt).Parse(value) + if err == nil { + parsedValue = &dt + } + case *DateValue: + var d DateValue + err = (&d).Parse(value) + if err == nil { + parsedValue = &d + } + case *TimeValue: + var t TimeValue + err = (&t).Parse(value) + if err == nil { + parsedValue = &t + } + default: if strings.HasPrefix(value, "@") { // File literals like @file.txt should work here @@ -501,6 +571,13 @@ func parseCLIArg[ } +// Ptr returns a pointer to its argument. It is used to initialize `Default` on pointer-typed +// Flag values, since Go does not allow taking the address of a composite literal's element +// or of an untyped constant. +func Ptr[T any](v T) *T { + return &v +} + // Assuming this string failed to parse as valid YAML, this function will // return true for strings that can reasonably be interpreted as a string literal, // like identifiers (`foo_bar`), UUIDs (`945b2f0c-8e89-487a-b02c-f851c69ea459`), @@ -594,6 +671,15 @@ func (c *cliValue[T]) String() string { // For basic types, use standard string representation return fmt.Sprintf("%v", v) + case *string, *int64, *float64, *bool, *DateTimeValue, *DateValue, *TimeValue: + // Pointer-to-primitive: nil renders as "null" (the CLI literal that produces it); + // non-nil derefs to the pointee's standard representation. + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "null" + } + return fmt.Sprintf("%v", rv.Elem().Interface()) + default: // For complex types, convert to YAML yamlBytes, err := yaml.MarshalWithOptions(c.value, yaml.Flow(true)) @@ -705,6 +791,15 @@ type SettableInnerField interface { SetInnerField(string, any) } +// InnerFieldSeeder lets an InnerFlag prepare its outer flag's underlying value +// before dispatching SetInnerField. This is only meaningful for Flag[any] — +// the codegen output for nullable complex schemas — whose untyped-nil zero +// value would otherwise have no reflect.Kind for the inner-field switch to +// dispatch on. +type InnerFieldSeeder interface { + SeedInnerCollection(isArrayOfObjects bool) +} + func (f *Flag[T]) SetInnerField(field string, val any) { if f.value == nil { f.value = &cliValue[T]{} @@ -718,6 +813,33 @@ func (f *Flag[T]) SetInnerField(field string, val any) { } } +// SeedInnerCollection initializes a Flag[any]'s underlying value as an empty +// map[string]any or []map[string]any so subsequent SetInnerField calls have a +// dispatchable reflect.Kind. For typed Flag[T] this is a no-op: the type +// assertion fails and the existing reflect.Kind on the typed-nil zero value +// already routes correctly. +func (f *Flag[T]) SeedInnerCollection(isArrayOfObjects bool) { + if f.value == nil { + f.value = &cliValue[T]{} + } + cv, ok := f.value.(*cliValue[T]) + if !ok { + return + } + if reflect.ValueOf(cv.value).Kind() != reflect.Invalid { + return + } + if isArrayOfObjects { + if seed, ok := any([]map[string]any{}).(T); ok { + cv.value = seed + } + return + } + if seed, ok := any(map[string]any{}).(T); ok { + cv.value = seed + } +} + func (c *cliValue[T]) SetInnerField(field string, val any) { flagVal := c.value flagValReflect := reflect.ValueOf(flagVal) diff --git a/internal/requestflag/requestflag_test.go b/internal/requestflag/requestflag_test.go index 0e86e07..06ffb72 100644 --- a/internal/requestflag/requestflag_test.go +++ b/internal/requestflag/requestflag_test.go @@ -1,6 +1,7 @@ package requestflag import ( + "encoding/json" "fmt" "testing" "time" @@ -616,6 +617,178 @@ func TestYamlHandling(t *testing.T) { }) } +// TestNullLiteralHandling pins how each Flag[T] type handles the literal value "null" +// when passed via the CLI. Pointer-typed flags serialize nil as JSON null, which is how +// nullable body fields (`anyOf: [T, null]` / `{nullable: true}`) let users clear a field +// via `--foo null`. Non-pointer primitive flags treat "null" as a raw value — these are +// non-nullable schemas where explicit null has no API semantics anyway. +func TestNullLiteralHandling(t *testing.T) { + t.Parallel() + + assertJSONBody := func(t *testing.T, value any, expected string) { + t.Helper() + body, err := json.Marshal(map[string]any{"foo": value}) + assert.NoError(t, err) + assert.JSONEq(t, expected, string(body)) + } + + t.Run("Flag[any] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[any]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[string] null is the raw string \"null\"", func(t *testing.T) { + t.Parallel() + cv := &cliValue[string]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":"null"}`) + }) + + t.Run("Flag[int64] null errors", func(t *testing.T) { + t.Parallel() + cv := &cliValue[int64]{} + assert.Error(t, cv.Set("null")) + }) + + t.Run("Flag[*string] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*string]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*string] value sends the string", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*string]{} + assert.NoError(t, cv.Set("1.1")) + assertJSONBody(t, cv.Get(), `{"foo":"1.1"}`) + }) + + t.Run("Flag[*int64] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*int64]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*int64] value sends the integer", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*int64]{} + assert.NoError(t, cv.Set("42")) + assertJSONBody(t, cv.Get(), `{"foo":42}`) + }) + + t.Run("Flag[*int64] invalid value errors", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*int64]{} + assert.Error(t, cv.Set("not-an-int")) + }) + + t.Run("Flag[*bool] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*bool]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*bool] value sends the boolean", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*bool]{} + assert.NoError(t, cv.Set("true")) + assertJSONBody(t, cv.Get(), `{"foo":true}`) + }) + + t.Run("Flag[*float64] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*float64]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*float64] value sends the float", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*float64]{} + assert.NoError(t, cv.Set("1.5")) + assertJSONBody(t, cv.Get(), `{"foo":1.5}`) + }) + + t.Run("Flag[*float64] invalid value errors", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*float64]{} + assert.Error(t, cv.Set("not-a-float")) + }) + + t.Run("Flag[*DateValue] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*DateValue]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*DateValue] value sends the date", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*DateValue]{} + assert.NoError(t, cv.Set("2023-05-15")) + assertJSONBody(t, cv.Get(), `{"foo":"2023-05-15"}`) + }) + + t.Run("Flag[*DateValue] invalid value errors", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*DateValue]{} + assert.Error(t, cv.Set("not-a-date")) + }) + + t.Run("Flag[*DateTimeValue] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*DateTimeValue]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*DateTimeValue] value sends the datetime", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*DateTimeValue]{} + assert.NoError(t, cv.Set("2023-05-15T14:30:45Z")) + assertJSONBody(t, cv.Get(), `{"foo":"2023-05-15T14:30:45Z"}`) + }) + + t.Run("Flag[*DateTimeValue] invalid value errors", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*DateTimeValue]{} + assert.Error(t, cv.Set("not-a-datetime")) + }) + + t.Run("Flag[*TimeValue] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*TimeValue]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) + + t.Run("Flag[*TimeValue] value sends the time", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*TimeValue]{} + assert.NoError(t, cv.Set("14:30:45")) + assertJSONBody(t, cv.Get(), `{"foo":"14:30:45"}`) + }) + + t.Run("Flag[*TimeValue] invalid value errors", func(t *testing.T) { + t.Parallel() + cv := &cliValue[*TimeValue]{} + assert.Error(t, cv.Set("not-a-time")) + }) + + // Nullable maps don't need pointer wrapping — a nil map already marshals as JSON null. + t.Run("Flag[map[string]any] null sends JSON null", func(t *testing.T) { + t.Parallel() + cv := &cliValue[map[string]any]{} + assert.NoError(t, cv.Set("null")) + assertJSONBody(t, cv.Get(), `{"foo":null}`) + }) +} + func TestFlagTypeNames(t *testing.T) { t.Parallel() @@ -646,3 +819,64 @@ func TestFlagTypeNames(t *testing.T) { }) } } + +// TestInnerFlagDispatchOnUntypedFlag pins inner-flag behavior for `Flag[any]`, +// which is the codegen output for nullable complex schemas (`anyOf: [T, null]` +// or `{nullable: true}`). The untyped-nil zero value carries no reflect.Kind, +// so SetInnerField has nowhere to dispatch the assignment — without explicit +// help the inner-field value silently drops. +func TestInnerFlagDispatchOnUntypedFlag(t *testing.T) { + t.Parallel() + + t.Run("nullable array of objects appends element from inner flag", func(t *testing.T) { + t.Parallel() + outer := &Flag[any]{Name: "mcp-server"} + assert.NoError(t, outer.PreParse()) + + nameFlag := &InnerFlag[string]{ + Name: "mcp-server.name", InnerField: "name", + OuterFlag: outer, OuterIsArrayOfObjects: true, + } + assert.NoError(t, nameFlag.Set("mcp-server.name", "first")) + + body, err := json.Marshal(map[string]any{"foo": outer.Get()}) + assert.NoError(t, err) + assert.JSONEq(t, `{"foo":[{"name":"first"}]}`, string(body)) + }) + + t.Run("nullable object sets field from inner flag", func(t *testing.T) { + t.Parallel() + outer := &Flag[any]{Name: "metadata"} + assert.NoError(t, outer.PreParse()) + + keyFlag := &InnerFlag[string]{ + Name: "metadata.key", InnerField: "key", OuterFlag: outer, + } + assert.NoError(t, keyFlag.Set("metadata.key", "value")) + + body, err := json.Marshal(map[string]any{"foo": outer.Get()}) + assert.NoError(t, err) + assert.JSONEq(t, `{"foo":{"key":"value"}}`, string(body)) + }) + + t.Run("multiple inner flags merge into the trailing element", func(t *testing.T) { + t.Parallel() + outer := &Flag[any]{Name: "mcp-server"} + assert.NoError(t, outer.PreParse()) + + nameFlag := &InnerFlag[string]{ + Name: "mcp-server.name", InnerField: "name", + OuterFlag: outer, OuterIsArrayOfObjects: true, + } + urlFlag := &InnerFlag[string]{ + Name: "mcp-server.url", InnerField: "url", + OuterFlag: outer, OuterIsArrayOfObjects: true, + } + assert.NoError(t, nameFlag.Set("mcp-server.name", "first")) + assert.NoError(t, urlFlag.Set("mcp-server.url", "https://example.com")) + + body, err := json.Marshal(map[string]any{"foo": outer.Get()}) + assert.NoError(t, err) + assert.JSONEq(t, `{"foo":[{"name":"first","url":"https://example.com"}]}`, string(body)) + }) +} diff --git a/pkg/cmd/audience.go b/pkg/cmd/audience.go index 1f3b207..9d908e4 100644 --- a/pkg/cmd/audience.go +++ b/pkg/cmd/audience.go @@ -37,7 +37,7 @@ var audiencesUpdate = requestflag.WithInnerFlags(cli.Command{ Name: "audience-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "description", Usage: "A description of the audience", BodyPath: "description", @@ -47,12 +47,12 @@ var audiencesUpdate = requestflag.WithInnerFlags(cli.Command{ Usage: "Filter configuration for audience membership containing an array of filter rules", BodyPath: "filter", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "name", Usage: "The name of the audience", BodyPath: "name", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "operator", Usage: "The logical operator (AND/OR) for the top-level filter", BodyPath: "operator", @@ -75,7 +75,7 @@ var audiencesList = cli.Command{ Usage: "Get the audiences associated with the authorization token.", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of audiences", QueryPath: "cursor", @@ -108,7 +108,7 @@ var audiencesListMembers = cli.Command{ Name: "audience-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of members", QueryPath: "cursor", diff --git a/pkg/cmd/auditevent.go b/pkg/cmd/auditevent.go index 42068f3..e8bdd5a 100644 --- a/pkg/cmd/auditevent.go +++ b/pkg/cmd/auditevent.go @@ -33,7 +33,7 @@ var auditEventsList = cli.Command{ Usage: "Fetch the list of audit events", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of audit events.", QueryPath: "cursor", diff --git a/pkg/cmd/automationinvoke.go b/pkg/cmd/automationinvoke.go index 28389bb..f716683 100644 --- a/pkg/cmd/automationinvoke.go +++ b/pkg/cmd/automationinvoke.go @@ -24,7 +24,7 @@ var automationsInvokeInvokeAdHoc = requestflag.WithInnerFlags(cli.Command{ Required: true, BodyPath: "automation", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "brand", BodyPath: "brand", }, @@ -36,11 +36,11 @@ var automationsInvokeInvokeAdHoc = requestflag.WithInnerFlags(cli.Command{ Name: "profile", BodyPath: "profile", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "recipient", BodyPath: "recipient", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "template", BodyPath: "template", }, @@ -53,7 +53,7 @@ var automationsInvokeInvokeAdHoc = requestflag.WithInnerFlags(cli.Command{ Name: "automation.steps", InnerField: "steps", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "automation.cancelation-token", InnerField: "cancelation_token", }, @@ -69,12 +69,12 @@ var automationsInvokeInvokeByTemplate = cli.Command{ Name: "template-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "recipient", Required: true, BodyPath: "recipient", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "brand", BodyPath: "brand", }, @@ -86,7 +86,7 @@ var automationsInvokeInvokeByTemplate = cli.Command{ Name: "profile", BodyPath: "profile", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "template", BodyPath: "template", }, diff --git a/pkg/cmd/brand.go b/pkg/cmd/brand.go index c3bd96c..c37f133 100644 --- a/pkg/cmd/brand.go +++ b/pkg/cmd/brand.go @@ -24,7 +24,7 @@ var brandsCreate = requestflag.WithInnerFlags(cli.Command{ Required: true, BodyPath: "name", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "id", BodyPath: "id", }, @@ -130,7 +130,7 @@ var brandsList = cli.Command{ Usage: "Get the list of brands.", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of brands.", QueryPath: "cursor", diff --git a/pkg/cmd/bulk.go b/pkg/cmd/bulk.go index a5f163a..784e272 100644 --- a/pkg/cmd/bulk.go +++ b/pkg/cmd/bulk.go @@ -47,7 +47,7 @@ var bulkAddUsers = requestflag.WithInnerFlags(cli.Command{ Usage: "User profile information. For email-based bulk jobs, `profile.email` is required \nfor provider routing to determine if the message can be delivered. The email \naddress should be provided here rather than in `to.email`.\n", InnerField: "profile", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "user.recipient", Usage: "User ID (legacy field, use profile or to.user_id instead)", InnerField: "recipient", @@ -80,11 +80,11 @@ var bulkCreateJob = requestflag.WithInnerFlags(cli.Command{ Usage: "Event ID or Notification ID (required). Can be either a \nNotification ID (e.g., \"FRH3QXM9E34W4RKP7MRC8NZ1T8V8\") or a custom Event ID \n(e.g., \"welcome-email\") mapped to a notification.\n", InnerField: "event", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "message.brand", InnerField: "brand", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[map[string]any]{ Name: "message.content", Usage: "Elemental content (optional, for V2 format). When provided, this will be used \ninstead of the notification associated with the `event` field.\n", InnerField: "content", @@ -101,7 +101,7 @@ var bulkCreateJob = requestflag.WithInnerFlags(cli.Command{ Name: "message.override", InnerField: "override", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "message.template", Usage: "Notification ID or template ID (optional, for V2 format). When provided, \nthis will be used instead of the notification associated with the `event` field.\n", InnerField: "template", @@ -118,7 +118,7 @@ var bulkListUsers = cli.Command{ Name: "job-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of users added to the bulk job", QueryPath: "cursor", diff --git a/pkg/cmd/inbound.go b/pkg/cmd/inbound.go index 50600c9..40a3b4f 100644 --- a/pkg/cmd/inbound.go +++ b/pkg/cmd/inbound.go @@ -42,7 +42,7 @@ var inboundTrackEvent = cli.Command{ Required: true, BodyPath: "type", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "user-id", Usage: "The user id associated with the track", BodyPath: "userId", diff --git a/pkg/cmd/list.go b/pkg/cmd/list.go index 6ba3eab..42d9023 100644 --- a/pkg/cmd/list.go +++ b/pkg/cmd/list.go @@ -67,12 +67,12 @@ var listsList = cli.Command{ Usage: "Returns all of the lists, with the ability to filter based on a pattern.", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next page of lists.", QueryPath: "cursor", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "pattern", Usage: "\"A pattern used to filter the list items returned. Pattern types supported: exact match on `list_id` or a pattern of one or more pattern parts. you may replace a part with either: `*` to match all parts in that position, or `**` to signify a wildcard `endsWith` pattern match.\"", QueryPath: "pattern", diff --git a/pkg/cmd/listsubscription.go b/pkg/cmd/listsubscription.go index 7f645c5..b8cf5b5 100644 --- a/pkg/cmd/listsubscription.go +++ b/pkg/cmd/listsubscription.go @@ -23,7 +23,7 @@ var listsSubscriptionsList = cli.Command{ Name: "list-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of list subscriptions", QueryPath: "cursor", diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index e478c52..e008c9f 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -33,72 +33,72 @@ var messagesList = cli.Command{ Usage: "Fetch the statuses of messages you've previously sent.", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*bool]{ Name: "archived", Usage: "A boolean value that indicates whether archived messages should be included in the response.", QueryPath: "archived", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of messages.", QueryPath: "cursor", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "enqueued-after", Usage: "The enqueued datetime of a message to filter out messages received before.", QueryPath: "enqueued_after", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "event", Usage: "A unique identifier representing the event that was used to send the event.", QueryPath: "event", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "list", Usage: "A unique identifier representing the list the message was sent to.", QueryPath: "list", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "message-id", Usage: "A unique identifier representing the message_id returned from either /send or /send/list.", QueryPath: "messageId", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "notification", Usage: "A unique identifier representing the notification that was used to send the event.", QueryPath: "notification", }, - &requestflag.Flag[[]any]{ + &requestflag.Flag[[]string]{ Name: "provider", Usage: "The key assocated to the provider you want to filter on. E.g., sendgrid, inbox, twilio, slack, msteams, etc. Allows multiple values to be set in query parameters.", QueryPath: "provider", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "recipient", Usage: "A unique identifier representing the recipient associated with the requested profile.", QueryPath: "recipient", }, - &requestflag.Flag[[]any]{ + &requestflag.Flag[[]string]{ Name: "status", Usage: "An indicator of the current status of the message. Allows multiple values to be set in query parameters.", QueryPath: "status", }, - &requestflag.Flag[[]any]{ + &requestflag.Flag[[]string]{ Name: "tag", Usage: "A tag placed in the metadata.tags during a notification send. Allows multiple values to be set in query parameters.", QueryPath: "tag", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "tags", Usage: "A comma delimited list of 'tags'. Messages will be returned if they match any of the tags passed in.", QueryPath: "tags", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "tenant-id", Usage: "Messages sent with the context of a Tenant", QueryPath: "tenant_id", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "trace-id", Usage: "The unique identifier used to trace the requests", QueryPath: "traceId", @@ -145,7 +145,7 @@ var messagesHistory = cli.Command{ Name: "message-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "type", Usage: "A supported Message History type that will filter the events returned.", QueryPath: "type", diff --git a/pkg/cmd/notification.go b/pkg/cmd/notification.go index 43ff6d5..d8896f0 100644 --- a/pkg/cmd/notification.go +++ b/pkg/cmd/notification.go @@ -92,7 +92,7 @@ var notificationsList = cli.Command{ Usage: "List notification templates in your workspace.", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Opaque pagination cursor from a previous response. Omit for the first page.", QueryPath: "cursor", @@ -102,7 +102,7 @@ var notificationsList = cli.Command{ Usage: "Filter to templates linked to this event map ID.", QueryPath: "event_id", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*bool]{ Name: "notes", Usage: "Include template notes in the response. Only applies to legacy templates.", QueryPath: "notes", diff --git a/pkg/cmd/profilelist.go b/pkg/cmd/profilelist.go index c6d3e76..48bd1b3 100644 --- a/pkg/cmd/profilelist.go +++ b/pkg/cmd/profilelist.go @@ -23,7 +23,7 @@ var profilesListsRetrieve = cli.Command{ Name: "user-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "A unique identifier that allows for fetching the next set of message statuses.", QueryPath: "cursor", diff --git a/pkg/cmd/routingstrategy.go b/pkg/cmd/routingstrategy.go index 5513892..3447e2f 100644 --- a/pkg/cmd/routingstrategy.go +++ b/pkg/cmd/routingstrategy.go @@ -34,7 +34,7 @@ var routingStrategiesCreate = requestflag.WithInnerFlags(cli.Command{ Name: "channels", BodyPath: "channels", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "description", Usage: "Optional description of the routing strategy.", BodyPath: "description", @@ -84,7 +84,7 @@ var routingStrategiesList = cli.Command{ Usage: "List routing strategies in your workspace. Returns metadata only (no\nrouting/channels/providers content). Use GET /routing-strategies/{id} for full\ndetails.", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Opaque pagination cursor from a previous response. Omit for the first page.", QueryPath: "cursor", @@ -123,7 +123,7 @@ var routingStrategiesListNotifications = cli.Command{ Name: "id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Opaque pagination cursor from a previous response. Omit for the first page.", QueryPath: "cursor", @@ -163,7 +163,7 @@ var routingStrategiesReplace = requestflag.WithInnerFlags(cli.Command{ Name: "channels", BodyPath: "channels", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "description", Usage: "Optional description. Omit or null to clear.", BodyPath: "description", diff --git a/pkg/cmd/send.go b/pkg/cmd/send.go index 7d98643..14bc54a 100644 --- a/pkg/cmd/send.go +++ b/pkg/cmd/send.go @@ -30,7 +30,7 @@ var sendMessage = requestflag.WithInnerFlags(cli.Command{ HideHelpCommand: true, }, map[string][]requestflag.HasOuterFlag{ "message": { - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "message.brand-id", InnerField: "brand_id", }, @@ -76,7 +76,7 @@ var sendMessage = requestflag.WithInnerFlags(cli.Command{ Usage: "Customize which channels/providers Courier may deliver the message through.", InnerField: "routing", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "message.template", InnerField: "template", }, diff --git a/pkg/cmd/tenant.go b/pkg/cmd/tenant.go index 4b4a600..8930950 100644 --- a/pkg/cmd/tenant.go +++ b/pkg/cmd/tenant.go @@ -43,7 +43,7 @@ var tenantsUpdate = requestflag.WithInnerFlags(cli.Command{ Required: true, BodyPath: "name", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "brand-id", Usage: "Brand to be used for the account when one is not specified by the send call.", BodyPath: "brand_id", @@ -52,7 +52,7 @@ var tenantsUpdate = requestflag.WithInnerFlags(cli.Command{ Name: "default-preferences", BodyPath: "default_preferences", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "parent-tenant-id", Usage: "Tenant's parent id (if any).", BodyPath: "parent_tenant_id", @@ -84,17 +84,17 @@ var tenantsList = cli.Command{ Usage: "Get a List of Tenants", Suggest: true, Flags: []cli.Flag{ - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Continue the pagination with the next cursor", QueryPath: "cursor", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*int64]{ Name: "limit", Usage: "The number of tenants to return \n(defaults to 20, maximum value of 100)", QueryPath: "limit", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "parent-tenant-id", Usage: "Filter the list of tenants by parent_id", QueryPath: "parent_tenant_id", @@ -127,12 +127,12 @@ var tenantsListUsers = cli.Command{ Name: "tenant-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Continue the pagination with the next cursor", QueryPath: "cursor", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*int64]{ Name: "limit", Usage: "The number of accounts to return \n(defaults to 20, maximum value of 100)", QueryPath: "limit", diff --git a/pkg/cmd/tenantpreferenceitem.go b/pkg/cmd/tenantpreferenceitem.go index c718f64..c19b074 100644 --- a/pkg/cmd/tenantpreferenceitem.go +++ b/pkg/cmd/tenantpreferenceitem.go @@ -36,7 +36,7 @@ var tenantsPreferencesItemsUpdate = cli.Command{ Usage: "The default channels to send to this tenant when has_custom_routing is enabled", BodyPath: "custom_routing", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*bool]{ Name: "has-custom-routing", Usage: "Override channel routing with custom preferences. This will override any template preferences that are set, but a user can still customize their preferences", BodyPath: "has_custom_routing", diff --git a/pkg/cmd/tenanttemplate.go b/pkg/cmd/tenanttemplate.go index 4ef793b..0338e55 100644 --- a/pkg/cmd/tenanttemplate.go +++ b/pkg/cmd/tenanttemplate.go @@ -41,12 +41,12 @@ var tenantsTemplatesList = cli.Command{ Name: "tenant-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Continue the pagination with the next cursor", QueryPath: "cursor", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*int64]{ Name: "limit", Usage: "The number of templates to return (defaults to 20, maximum value of 100)", QueryPath: "limit", diff --git a/pkg/cmd/userpreference.go b/pkg/cmd/userpreference.go index bf711e8..0c6ff3d 100644 --- a/pkg/cmd/userpreference.go +++ b/pkg/cmd/userpreference.go @@ -23,7 +23,7 @@ var usersPreferencesRetrieve = cli.Command{ Name: "user-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "tenant-id", Usage: "Query the preferences of a user for this specific tenant context.", QueryPath: "tenant_id", @@ -46,7 +46,7 @@ var usersPreferencesRetrieveTopic = cli.Command{ Name: "topic-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "tenant-id", Usage: "Query the preferences of a user for this specific tenant context.", QueryPath: "tenant_id", @@ -74,7 +74,7 @@ var usersPreferencesUpdateOrCreateTopic = requestflag.WithInnerFlags(cli.Command Required: true, BodyPath: "topic", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "tenant-id", Usage: "Update the preferences of a user for this specific tenant context.", QueryPath: "tenant_id", @@ -94,7 +94,7 @@ var usersPreferencesUpdateOrCreateTopic = requestflag.WithInnerFlags(cli.Command Usage: "The Channels a user has chosen to receive notifications through for this topic", InnerField: "custom_routing", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*bool]{ Name: "topic.has-custom-routing", InnerField: "has_custom_routing", }, diff --git a/pkg/cmd/usertenant.go b/pkg/cmd/usertenant.go index ab6c77a..a2d830a 100644 --- a/pkg/cmd/usertenant.go +++ b/pkg/cmd/usertenant.go @@ -23,12 +23,12 @@ var usersTenantsList = cli.Command{ Name: "user-id", Required: true, }, - &requestflag.Flag[any]{ + &requestflag.Flag[*string]{ Name: "cursor", Usage: "Continue the pagination with the next cursor", QueryPath: "cursor", }, - &requestflag.Flag[any]{ + &requestflag.Flag[*int64]{ Name: "limit", Usage: "The number of accounts to return \n(defaults to 20, maximum value of 100)", QueryPath: "limit", @@ -67,12 +67,12 @@ var usersTenantsAddMultiple = requestflag.WithInnerFlags(cli.Command{ Usage: "Additional metadata to be applied to a user profile when used in a tenant context", InnerField: "profile", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "tenant.type", Usage: `Allowed values: "user".`, InnerField: "type", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "tenant.user-id", Usage: "User ID for the association between tenant and user", InnerField: "user_id", diff --git a/pkg/cmd/usertoken.go b/pkg/cmd/usertoken.go index 4776d7a..acb0b94 100644 --- a/pkg/cmd/usertoken.go +++ b/pkg/cmd/usertoken.go @@ -65,7 +65,7 @@ var usersTokensUpdate = requestflag.WithInnerFlags(cli.Command{ Usage: "The JSON path specifying the part of the profile to operate on.", InnerField: "path", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "patch.value", Usage: "The value for the operation.", InnerField: "value", @@ -163,54 +163,54 @@ var usersTokensAddSingle = requestflag.WithInnerFlags(cli.Command{ HideHelpCommand: true, }, map[string][]requestflag.HasOuterFlag{ "device": { - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "device.ad-id", Usage: "Id of the advertising identifier", InnerField: "ad_id", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "device.app-id", Usage: "Id of the application the token is used for", InnerField: "app_id", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "device.device-id", Usage: "Id of the device the token is associated with", InnerField: "device_id", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "device.manufacturer", Usage: "The device manufacturer", InnerField: "manufacturer", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "device.model", Usage: "The device model", InnerField: "model", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "device.platform", Usage: "The device platform i.e. android, ios, web", InnerField: "platform", }, }, "tracking": { - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "tracking.ip", Usage: "The IP address of the device", InnerField: "ip", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "tracking.lat", Usage: "The latitude of the device", InnerField: "lat", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "tracking.long", Usage: "The longitude of the device", InnerField: "long", }, - &requestflag.InnerFlag[any]{ + &requestflag.InnerFlag[*string]{ Name: "tracking.os-version", Usage: "The operating system version", InnerField: "os_version", From 29f9b3f993fbd72360b1ce65e77b26e03bbfce8f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:23:29 +0000 Subject: [PATCH 17/29] feat: support passing path and query params over stdin --- internal/requestflag/requestflag.go | 115 +++++++- internal/requestflag/requestflag_test.go | 345 +++++++++++++++++++++++ pkg/cmd/audience.go | 32 ++- pkg/cmd/auditevent.go | 9 +- pkg/cmd/auth.go | 4 +- pkg/cmd/automation.go | 4 +- pkg/cmd/automationinvoke.go | 13 +- pkg/cmd/brand.go | 27 +- pkg/cmd/bulk.go | 32 ++- pkg/cmd/flagoptions.go | 48 +++- pkg/cmd/inbound.go | 4 +- pkg/cmd/journey.go | 13 +- pkg/cmd/list.go | 32 ++- pkg/cmd/listsubscription.go | 63 +++-- pkg/cmd/message.go | 28 +- pkg/cmd/notification.go | 103 ++++--- pkg/cmd/notificationcheck.go | 54 ++-- pkg/cmd/profile.go | 37 +-- pkg/cmd/profilelist.go | 23 +- pkg/cmd/provider.go | 27 +- pkg/cmd/providercatalog.go | 4 +- pkg/cmd/request.go | 5 +- pkg/cmd/routingstrategy.go | 36 +-- pkg/cmd/send.go | 4 +- pkg/cmd/tenant.go | 32 ++- pkg/cmd/tenantpreferenceitem.go | 36 +-- pkg/cmd/tenanttemplate.go | 63 +++-- pkg/cmd/tenanttemplateversion.go | 25 +- pkg/cmd/translation.go | 36 +-- pkg/cmd/userpreference.go | 45 +-- pkg/cmd/usertenant.go | 59 ++-- pkg/cmd/usertoken.go | 82 +++--- 32 files changed, 1015 insertions(+), 425 deletions(-) diff --git a/internal/requestflag/requestflag.go b/internal/requestflag/requestflag.go index 54c2509..77c4f1f 100644 --- a/internal/requestflag/requestflag.go +++ b/internal/requestflag/requestflag.go @@ -1,6 +1,7 @@ package requestflag import ( + "encoding/json" "fmt" "reflect" "strconv" @@ -12,6 +13,26 @@ import ( "github.com/urfave/cli/v3" ) +// formatForFlagSet converts a Go value parsed from YAML/JSON stdin data into a string +// that flag.Set (and thus parseCLIArg) can parse correctly for each flag type. +// Strings are returned as-is (parseCLIArg[string] assigns the raw value directly, so +// JSON-quoting must be avoided). Scalars use %v. Complex types (maps, slices) are +// JSON-encoded, which the yaml.Unmarshal default branch in parseCLIArg can parse. +func formatForFlagSet(val any) (string, error) { + switch v := val.(type) { + case string: + return v, nil + case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: + return fmt.Sprintf("%v", val), nil + default: + b, err := json.Marshal(val) + if err != nil { + return "", fmt.Errorf("cannot format value %T for flag.Set: %w", val, err) + } + return string(b), nil + } +} + // Flag [T] is a generic flag base which can be used to implement the most // common interfaces used by urfave/cli. Additionally, it allows specifying // where in an HTTP request the flag values should be placed (e.g. query, body, etc.). @@ -41,6 +62,7 @@ type Flag[ HeaderPath string // location in the request header to put this flag's value BodyPath string // location in the request body to put this flag's value BodyRoot bool // if true, then use this value as the entire request body + PathParam string // name of the URL path parameter this flag's value maps to // Const, when true, marks this flag as a constant. The flag's Default value is used as the fixed value // and always included in the request (IsSet returns true). The user can still see and override the flag, @@ -72,6 +94,7 @@ type InRequest interface { GetQueryPath() string GetHeaderPath() string GetBodyPath() string + GetPathParam() string IsBodyRoot() bool IsFileInput() bool GetDataAliases() []string @@ -89,6 +112,10 @@ func (f Flag[T]) GetBodyPath() string { return f.BodyPath } +func (f Flag[T]) GetPathParam() string { + return f.PathParam +} + func (f Flag[T]) IsBodyRoot() bool { return f.BodyRoot } @@ -108,7 +135,91 @@ type RequestContents struct { Body any } -// Extract query parameters, headers, and body values from command flags. +// ApplyStdinDataToFlags sets flag values from a parsed stdin data map for flags that have not already been +// set via the command line. This allows piped YAML/JSON data to satisfy path, query, and header parameters. +// Body parameters are excluded: they are already handled by the maps.Copy merge in flagOptions. +// For each unset flag, if the parsed data map contains a key matching the flag's QueryPath, HeaderPath, or +// PathParam (or any of its DataAliases), the flag is set to that value via flag.Set. +// +// Inner flags (those with an outer flag) are also handled: if the outer flag's body path key exists in the +// data map and contains a nested map with a key matching the inner flag's field (or aliases), the inner +// flag is set from that nested value. +func ApplyStdinDataToFlags(cmd *cli.Command, data map[string]any) error { + for _, flag := range cmd.Flags { + if flag.IsSet() { + continue + } + + // Handle inner flags: look for their value nested under the outer flag's body path. + if inner, ok := flag.(HasOuterFlag); ok { + outer, outerOk := inner.GetOuterFlag().(InRequest) + if !outerOk || outer.GetBodyPath() == "" { + continue + } + nested, ok := data[outer.GetBodyPath()].(map[string]any) + if !ok { + continue + } + innerField := inner.GetInnerField() + val, found := nested[innerField] + if !found { + for _, alias := range inner.GetDataAliases() { + if alias != "" && alias != innerField { + if v, ok := nested[alias]; ok { + val, found = v, true + break + } + } + } + } + if !found { + continue + } + setVal, err := formatForFlagSet(val) + if err != nil { + return fmt.Errorf("cannot format piped value for flag %q: %w", flag.Names()[0], err) + } + if err := flag.Set(flag.Names()[0], setVal); err != nil { + return fmt.Errorf("cannot set flag %q from piped data: %w", flag.Names()[0], err) + } + continue + } + + inReq, ok := flag.(InRequest) + if !ok { + continue + } + + // Try each request location in turn, checking the canonical path key and all aliases. + // Body params are excluded: they are already handled by the maps.Copy merge in flagOptions. + for _, path := range []string{inReq.GetQueryPath(), inReq.GetHeaderPath(), inReq.GetPathParam()} { + if path == "" { + continue + } + var val any + var found bool + for _, key := range append([]string{path}, inReq.GetDataAliases()...) { + if v, ok := data[key]; ok { + val, found = v, true + break + } + } + if !found { + continue + } + setVal, err := formatForFlagSet(val) + if err != nil { + return fmt.Errorf("cannot format piped value for flag %q: %w", flag.Names()[0], err) + } + if err := flag.Set(flag.Names()[0], setVal); err != nil { + return fmt.Errorf("cannot set flag %q from piped data: %w", flag.Names()[0], err) + } + break + } + } + return nil +} + func ExtractRequestContents(cmd *cli.Command) RequestContents { bodyMap := make(map[string]any) res := RequestContents{ @@ -291,7 +402,7 @@ func (f *Flag[T]) IsRequired() bool { } // Intentionally don't use `f.Required`, because request flags may be passed // over stdin as well as by flag. - if f.BodyPath != "" || f.BodyRoot { + if f.BodyPath != "" || f.BodyRoot || f.PathParam != "" || f.QueryPath != "" || f.HeaderPath != "" { return false } return f.Required diff --git a/internal/requestflag/requestflag_test.go b/internal/requestflag/requestflag_test.go index 06ffb72..779bd57 100644 --- a/internal/requestflag/requestflag_test.go +++ b/internal/requestflag/requestflag_test.go @@ -880,3 +880,348 @@ func TestInnerFlagDispatchOnUntypedFlag(t *testing.T) { assert.JSONEq(t, `{"foo":[{"name":"first","url":"https://example.com"}]}`, string(body)) }) } + +func TestApplyStdinDataToFlags(t *testing.T) { + t.Parallel() + + t.Run("sets query path flag from piped data", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"account_id": "acct_123"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, flag.IsSet()) + assert.Equal(t, "acct_123", flag.Get()) + }) + + t.Run("sets header path flag from piped data", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "idempotency-key", + HeaderPath: "Idempotency-Key", + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"Idempotency-Key": "key-xyz"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, flag.IsSet()) + assert.Equal(t, "key-xyz", flag.Get()) + }) + + t.Run("does not set body path flag from piped data", func(t *testing.T) { + t.Parallel() + + // Body params are handled by the maps.Copy merge in flagOptions, not by ApplyStdinDataToFlags. + flag := &Flag[string]{ + Name: "message", + BodyPath: "message", + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"message": "hello world"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.False(t, flag.IsSet()) + }) + + t.Run("does not override flag already set via CLI", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + } + assert.NoError(t, flag.PreParse()) + assert.NoError(t, flag.Set("account-id", "explicit_value")) + + data := map[string]any{"account_id": "piped_value"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + // The explicitly-set value should win. + assert.Equal(t, "explicit_value", flag.Get()) + }) + + t.Run("sets integer query flag from piped data", func(t *testing.T) { + t.Parallel() + + flag := &Flag[int64]{ + Name: "page-size", + QueryPath: "page_size", + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"page_size": int64(50)} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, flag.IsSet()) + assert.Equal(t, int64(50), flag.Get()) + }) + + t.Run("sets boolean query flag from piped data", func(t *testing.T) { + t.Parallel() + + flag := &Flag[bool]{ + Name: "include-deleted", + QueryPath: "include_deleted", + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"include_deleted": true} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, flag.IsSet()) + assert.Equal(t, true, flag.Get()) + }) + + t.Run("resolves query path flag via data alias", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + DataAliases: []string{"accountId", "account"}, + } + assert.NoError(t, flag.PreParse()) + + // Use one of the aliases as the key in piped data. + data := map[string]any{"accountId": "acct_alias"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, flag.IsSet()) + assert.Equal(t, "acct_alias", flag.Get()) + }) + + t.Run("does not set body path flag via data alias", func(t *testing.T) { + t.Parallel() + + // Body params are handled by the maps.Copy merge in flagOptions, not by ApplyStdinDataToFlags. + flag := &Flag[string]{ + Name: "user-name", + BodyPath: "user_name", + DataAliases: []string{"userName", "username"}, + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"userName": "alice"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.False(t, flag.IsSet()) + }) + + t.Run("ignores flags with no matching key in piped data", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"other_key": "value"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.False(t, flag.IsSet()) + }) + + t.Run("ignores flags with no path set", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "some-flag", + // No QueryPath, HeaderPath, or BodyPath + } + assert.NoError(t, flag.PreParse()) + + data := map[string]any{"some-flag": "value"} + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.False(t, flag.IsSet()) + }) + + t.Run("handles multiple flags from piped data", func(t *testing.T) { + t.Parallel() + + accountFlag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + } + limitFlag := &Flag[int64]{ + Name: "limit", + QueryPath: "limit", + } + assert.NoError(t, accountFlag.PreParse()) + assert.NoError(t, limitFlag.PreParse()) + + data := map[string]any{ + "account_id": "acct_abc", + "limit": int64(25), + } + cmd := &cli.Command{Flags: []cli.Flag{accountFlag, limitFlag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, accountFlag.IsSet()) + assert.Equal(t, "acct_abc", accountFlag.Get()) + assert.True(t, limitFlag.IsSet()) + assert.Equal(t, int64(25), limitFlag.Get()) + }) + + t.Run("sets inner flag from nested piped data under outer body path", func(t *testing.T) { + t.Parallel() + + outer := &Flag[map[string]any]{ + Name: "address", + BodyPath: "address", + } + assert.NoError(t, outer.PreParse()) + + cityInner := &InnerFlag[string]{ + Name: "address.city", + InnerField: "city", + OuterFlag: outer, + } + + data := map[string]any{ + "address": map[string]any{"city": "San Francisco"}, + } + cmd := &cli.Command{Flags: []cli.Flag{outer, cityInner}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + // InnerFlag.IsSet() is always false by design; verify the value was written + // into the outer flag's underlying map instead. + outerVal, ok := outer.Get().(map[string]any) + assert.True(t, ok, "expected outer flag value to be map[string]any, got %T", outer.Get()) + assert.Equal(t, "San Francisco", outerVal["city"]) + }) + + t.Run("sets inner flag via data alias in nested piped data", func(t *testing.T) { + t.Parallel() + + outer := &Flag[map[string]any]{ + Name: "address", + BodyPath: "address", + } + assert.NoError(t, outer.PreParse()) + + cityInner := &InnerFlag[string]{ + Name: "address.city", + InnerField: "city", + DataAliases: []string{"cityName"}, + OuterFlag: outer, + } + + // Use the alias in piped data. + data := map[string]any{ + "address": map[string]any{"cityName": "Portland"}, + } + cmd := &cli.Command{Flags: []cli.Flag{outer, cityInner}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + // InnerFlag.IsSet() is always false by design; verify the value was written + // into the outer flag's underlying map instead. + outerVal, ok := outer.Get().(map[string]any) + assert.True(t, ok, "expected outer flag value to be map[string]any, got %T", outer.Get()) + assert.Equal(t, "Portland", outerVal["city"]) + }) + + t.Run("does not set inner flag when outer flag has no body path", func(t *testing.T) { + t.Parallel() + + outer := &Flag[map[string]any]{ + Name: "options", + // No BodyPath set + } + assert.NoError(t, outer.PreParse()) + + inner := &InnerFlag[string]{ + Name: "options.key", + InnerField: "key", + OuterFlag: outer, + } + + data := map[string]any{ + "options": map[string]any{"key": "value"}, + } + cmd := &cli.Command{Flags: []cli.Flag{outer, inner}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.False(t, inner.IsSet()) + }) + + t.Run("does not set inner flag when piped data has no nested map for outer path", func(t *testing.T) { + t.Parallel() + + outer := &Flag[map[string]any]{ + Name: "address", + BodyPath: "address", + } + assert.NoError(t, outer.PreParse()) + + inner := &InnerFlag[string]{ + Name: "address.city", + InnerField: "city", + OuterFlag: outer, + } + + // The outer body path key is missing from the piped data. + data := map[string]any{"other": "value"} + cmd := &cli.Command{Flags: []cli.Flag{outer, inner}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.False(t, inner.IsSet()) + }) + + t.Run("canonical path key takes precedence over alias when both are present", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + DataAliases: []string{"accountId"}, + } + assert.NoError(t, flag.PreParse()) + + // Both canonical and alias present — canonical should win because it's checked first. + data := map[string]any{ + "account_id": "canonical_value", + "accountId": "alias_value", + } + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, data)) + + assert.True(t, flag.IsSet()) + assert.Equal(t, "canonical_value", flag.Get()) + }) + + t.Run("empty data map does not set any flags", func(t *testing.T) { + t.Parallel() + + flag := &Flag[string]{ + Name: "account-id", + QueryPath: "account_id", + } + assert.NoError(t, flag.PreParse()) + + cmd := &cli.Command{Flags: []cli.Flag{flag}} + assert.NoError(t, ApplyStdinDataToFlags(cmd, map[string]any{})) + + assert.False(t, flag.IsSet()) + }) +} diff --git a/pkg/cmd/audience.go b/pkg/cmd/audience.go index 9d908e4..7a7ff8f 100644 --- a/pkg/cmd/audience.go +++ b/pkg/cmd/audience.go @@ -20,8 +20,9 @@ var audiencesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "audience-id", - Required: true, + Name: "audience-id", + Required: true, + PathParam: "audience_id", }, }, Action: handleAudiencesRetrieve, @@ -34,8 +35,9 @@ var audiencesUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "audience-id", - Required: true, + Name: "audience-id", + Required: true, + PathParam: "audience_id", }, &requestflag.Flag[*string]{ Name: "description", @@ -91,8 +93,9 @@ var audiencesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "audience-id", - Required: true, + Name: "audience-id", + Required: true, + PathParam: "audience_id", }, }, Action: handleAudiencesDelete, @@ -105,8 +108,9 @@ var audiencesListMembers = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "audience-id", - Required: true, + Name: "audience-id", + Required: true, + PathParam: "audience_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -171,8 +175,6 @@ func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AudienceUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -184,6 +186,8 @@ func handleAudiencesUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.AudienceUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Audiences.Update( @@ -217,8 +221,6 @@ func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AudienceListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -230,6 +232,8 @@ func handleAudiencesList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.AudienceListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Audiences.List(ctx, params, options...) @@ -286,8 +290,6 @@ func handleAudiencesListMembers(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AudienceListMembersParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -299,6 +301,8 @@ func handleAudiencesListMembers(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.AudienceListMembersParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Audiences.ListMembers( diff --git a/pkg/cmd/auditevent.go b/pkg/cmd/auditevent.go index e8bdd5a..559731e 100644 --- a/pkg/cmd/auditevent.go +++ b/pkg/cmd/auditevent.go @@ -20,8 +20,9 @@ var auditEventsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "audit-event-id", - Required: true, + Name: "audit-event-id", + Required: true, + PathParam: "audit-event-id", }, }, Action: handleAuditEventsRetrieve, @@ -93,8 +94,6 @@ func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AuditEventListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -106,6 +105,8 @@ func handleAuditEventsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.AuditEventListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.AuditEvents.List(ctx, params, options...) diff --git a/pkg/cmd/auth.go b/pkg/cmd/auth.go index 22845cc..3cc9182 100644 --- a/pkg/cmd/auth.go +++ b/pkg/cmd/auth.go @@ -44,8 +44,6 @@ func handleAuthIssueToken(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AuthIssueTokenParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -57,6 +55,8 @@ func handleAuthIssueToken(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.AuthIssueTokenParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Auth.IssueToken(ctx, params, options...) diff --git a/pkg/cmd/automation.go b/pkg/cmd/automation.go index 7ea138b..dddde80 100644 --- a/pkg/cmd/automation.go +++ b/pkg/cmd/automation.go @@ -43,8 +43,6 @@ func handleAutomationsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AutomationListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -56,6 +54,8 @@ func handleAutomationsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.AutomationListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Automations.List(ctx, params, options...) diff --git a/pkg/cmd/automationinvoke.go b/pkg/cmd/automationinvoke.go index f716683..e05f527 100644 --- a/pkg/cmd/automationinvoke.go +++ b/pkg/cmd/automationinvoke.go @@ -66,8 +66,9 @@ var automationsInvokeInvokeByTemplate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "templateId", }, &requestflag.Flag[*string]{ Name: "recipient", @@ -103,8 +104,6 @@ func handleAutomationsInvokeInvokeAdHoc(ctx context.Context, cmd *cli.Command) e return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AutomationInvokeInvokeAdHocParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -116,6 +115,8 @@ func handleAutomationsInvokeInvokeAdHoc(ctx context.Context, cmd *cli.Command) e return err } + params := courier.AutomationInvokeInvokeAdHocParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Automations.Invoke.InvokeAdHoc(ctx, params, options...) @@ -147,8 +148,6 @@ func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Comma return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.AutomationInvokeInvokeByTemplateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -160,6 +159,8 @@ func handleAutomationsInvokeInvokeByTemplate(ctx context.Context, cmd *cli.Comma return err } + params := courier.AutomationInvokeInvokeByTemplateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Automations.Invoke.InvokeByTemplate( diff --git a/pkg/cmd/brand.go b/pkg/cmd/brand.go index c37f133..f2180e5 100644 --- a/pkg/cmd/brand.go +++ b/pkg/cmd/brand.go @@ -68,8 +68,9 @@ var brandsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brand_id", }, }, Action: handleBrandsRetrieve, @@ -82,8 +83,9 @@ var brandsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brand_id", }, &requestflag.Flag[string]{ Name: "name", @@ -146,8 +148,9 @@ var brandsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "brand-id", - Required: true, + Name: "brand-id", + Required: true, + PathParam: "brand_id", }, }, Action: handleBrandsDelete, @@ -162,8 +165,6 @@ func handleBrandsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.BrandNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -175,6 +176,8 @@ func handleBrandsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.BrandNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Brands.New(ctx, params, options...) @@ -248,8 +251,6 @@ func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.BrandUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -261,6 +262,8 @@ func handleBrandsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.BrandUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Brands.Update( @@ -294,8 +297,6 @@ func handleBrandsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.BrandListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -307,6 +308,8 @@ func handleBrandsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.BrandListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Brands.List(ctx, params, options...) diff --git a/pkg/cmd/bulk.go b/pkg/cmd/bulk.go index 784e272..2fd110c 100644 --- a/pkg/cmd/bulk.go +++ b/pkg/cmd/bulk.go @@ -20,8 +20,9 @@ var bulkAddUsers = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "job-id", - Required: true, + Name: "job-id", + Required: true, + PathParam: "job_id", }, &requestflag.Flag[[]map[string]any]{ Name: "user", @@ -115,8 +116,9 @@ var bulkListUsers = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "job-id", - Required: true, + Name: "job-id", + Required: true, + PathParam: "job_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -134,8 +136,9 @@ var bulkRetrieveJob = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "job-id", - Required: true, + Name: "job-id", + Required: true, + PathParam: "job_id", }, }, Action: handleBulkRetrieveJob, @@ -148,8 +151,9 @@ var bulkRunJob = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "job-id", - Required: true, + Name: "job-id", + Required: true, + PathParam: "job_id", }, }, Action: handleBulkRunJob, @@ -167,8 +171,6 @@ func handleBulkAddUsers(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.BulkAddUsersParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -180,6 +182,8 @@ func handleBulkAddUsers(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.BulkAddUsersParams{} + return client.Bulk.AddUsers( ctx, cmd.Value("job-id").(string), @@ -196,8 +200,6 @@ func handleBulkCreateJob(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.BulkNewJobParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -209,6 +211,8 @@ func handleBulkCreateJob(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.BulkNewJobParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Bulk.NewJob(ctx, params, options...) @@ -240,8 +244,6 @@ func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.BulkListUsersParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -253,6 +255,8 @@ func handleBulkListUsers(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.BulkListUsersParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Bulk.ListUsers( diff --git a/pkg/cmd/flagoptions.go b/pkg/cmd/flagoptions.go index 7b63627..e0d84cc 100644 --- a/pkg/cmd/flagoptions.go +++ b/pkg/cmd/flagoptions.go @@ -339,7 +339,7 @@ func flagOptions( } stdinConsumedByPipe := false - if (bodyType == MultipartFormEncoded || bodyType == ApplicationJSON) && !ignoreStdin && isInputPiped() { + if bodyType != ApplicationOctetStream && !ignoreStdin && isInputPiped() { pipeData, err := io.ReadAll(os.Stdin) if err != nil { return nil, err @@ -353,16 +353,45 @@ func flagOptions( } if bodyMap, ok := bodyData.(map[string]any); ok { applyDataAliases(cmd, bodyMap) - if flagMap, ok := requestContents.Body.(map[string]any); ok { - maps.Copy(bodyMap, flagMap) - requestContents.Body = bodyMap + // Apply any matching keys from the piped data to path, query, and header flags + // that have not already been set via the command line. + if err := requestflag.ApplyStdinDataToFlags(cmd, bodyMap); err != nil { + return nil, err + } + // Re-extract request contents now that flags may have been updated. + requestContents = requestflag.ExtractRequestContents(cmd) + // Remove keys that were consumed as query, header, or path params so they + // don't also leak into the request body via the maps.Copy merge below. + // We delete both the canonical key and any aliases since the user may have + // piped data using an alias name rather than the canonical API name. + for _, flag := range cmd.Flags { + inReq, ok := flag.(requestflag.InRequest) + if !ok || !flag.IsSet() { + continue + } + if inReq.GetQueryPath() != "" || inReq.GetHeaderPath() != "" || inReq.GetPathParam() != "" { + delete(bodyMap, inReq.GetQueryPath()) + delete(bodyMap, inReq.GetHeaderPath()) + delete(bodyMap, inReq.GetPathParam()) + for _, alias := range inReq.GetDataAliases() { + delete(bodyMap, alias) + } + } + } + if bodyType != EmptyBody { + if flagMap, ok := requestContents.Body.(map[string]any); ok { + maps.Copy(bodyMap, flagMap) + requestContents.Body = bodyMap + } else { + bodyData = requestContents.Body + } + } + } else if bodyType != EmptyBody { + if flagMap, ok := requestContents.Body.(map[string]any); ok && len(flagMap) > 0 { + return nil, fmt.Errorf("Cannot merge flags with a body that is not a map: %v", bodyData) } else { - bodyData = requestContents.Body + requestContents.Body = bodyData } - } else if flagMap, ok := requestContents.Body.(map[string]any); ok && len(flagMap) > 0 { - return nil, fmt.Errorf("Cannot merge flags with a body that is not a map: %v", bodyData) - } else { - requestContents.Body = bodyData } } } @@ -370,7 +399,6 @@ func flagOptions( if missingFlags := requestflag.GetMissingRequiredFlags(cmd, requestContents.Body); len(missingFlags) > 0 { if len(missingFlags) == 1 { return nil, fmt.Errorf("Required flag %q not set\nRun '%s --help' for usage information", missingFlags[0].Names()[0], cmd.FullName()) - } else { names := []string{} for _, flag := range missingFlags { diff --git a/pkg/cmd/inbound.go b/pkg/cmd/inbound.go index 40a3b4f..8be3d1f 100644 --- a/pkg/cmd/inbound.go +++ b/pkg/cmd/inbound.go @@ -60,8 +60,6 @@ func handleInboundTrackEvent(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.InboundTrackEventParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -73,6 +71,8 @@ func handleInboundTrackEvent(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.InboundTrackEventParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Inbound.TrackEvent(ctx, params, options...) diff --git a/pkg/cmd/journey.go b/pkg/cmd/journey.go index f371bf2..f270cc1 100644 --- a/pkg/cmd/journey.go +++ b/pkg/cmd/journey.go @@ -41,8 +41,9 @@ var journeysInvoke = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "templateId", }, &requestflag.Flag[map[string]any]{ Name: "data", @@ -72,8 +73,6 @@ func handleJourneysList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.JourneyListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -85,6 +84,8 @@ func handleJourneysList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.JourneyListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Journeys.List(ctx, params, options...) @@ -116,8 +117,6 @@ func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.JourneyInvokeParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -129,6 +128,8 @@ func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.JourneyInvokeParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Journeys.Invoke( diff --git a/pkg/cmd/list.go b/pkg/cmd/list.go index 42d9023..28ac970 100644 --- a/pkg/cmd/list.go +++ b/pkg/cmd/list.go @@ -20,8 +20,9 @@ var listsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, }, Action: handleListsRetrieve, @@ -34,8 +35,9 @@ var listsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, &requestflag.Flag[string]{ Name: "name", @@ -88,8 +90,9 @@ var listsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, }, Action: handleListsDelete, @@ -102,8 +105,9 @@ var listsRestore = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, }, Action: handleListsRestore, @@ -163,8 +167,6 @@ func handleListsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -176,6 +178,8 @@ func handleListsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ListUpdateParams{} + return client.Lists.Update( ctx, cmd.Value("list-id").(string), @@ -192,8 +196,6 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -205,6 +207,8 @@ func handleListsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ListListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Lists.List(ctx, params, options...) @@ -261,8 +265,6 @@ func handleListsRestore(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListRestoreParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -274,6 +276,8 @@ func handleListsRestore(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ListRestoreParams{} + return client.Lists.Restore( ctx, cmd.Value("list-id").(string), diff --git a/pkg/cmd/listsubscription.go b/pkg/cmd/listsubscription.go index b8cf5b5..992b75c 100644 --- a/pkg/cmd/listsubscription.go +++ b/pkg/cmd/listsubscription.go @@ -20,8 +20,9 @@ var listsSubscriptionsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -39,8 +40,9 @@ var listsSubscriptionsAdd = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, &requestflag.Flag[[]map[string]any]{ Name: "recipient", @@ -69,8 +71,9 @@ var listsSubscriptionsSubscribe = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, &requestflag.Flag[[]map[string]any]{ Name: "recipient", @@ -99,12 +102,14 @@ var listsSubscriptionsSubscribeUser = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[map[string]any]{ Name: "preferences", @@ -132,12 +137,14 @@ var listsSubscriptionsUnsubscribeUser = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "list-id", - Required: true, + Name: "list-id", + Required: true, + PathParam: "list_id", }, &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleListsSubscriptionsUnsubscribeUser, @@ -155,8 +162,6 @@ func handleListsSubscriptionsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListSubscriptionListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -168,6 +173,8 @@ func handleListsSubscriptionsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ListSubscriptionListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Lists.Subscriptions.List( @@ -204,8 +211,6 @@ func handleListsSubscriptionsAdd(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListSubscriptionAddParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -217,6 +222,8 @@ func handleListsSubscriptionsAdd(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ListSubscriptionAddParams{} + return client.Lists.Subscriptions.Add( ctx, cmd.Value("list-id").(string), @@ -236,8 +243,6 @@ func handleListsSubscriptionsSubscribe(ctx context.Context, cmd *cli.Command) er return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListSubscriptionSubscribeParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -249,6 +254,8 @@ func handleListsSubscriptionsSubscribe(ctx context.Context, cmd *cli.Command) er return err } + params := courier.ListSubscriptionSubscribeParams{} + return client.Lists.Subscriptions.Subscribe( ctx, cmd.Value("list-id").(string), @@ -268,10 +275,6 @@ func handleListsSubscriptionsSubscribeUser(ctx context.Context, cmd *cli.Command return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListSubscriptionSubscribeUserParams{ - ListID: cmd.Value("list-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -283,6 +286,10 @@ func handleListsSubscriptionsSubscribeUser(ctx context.Context, cmd *cli.Command return err } + params := courier.ListSubscriptionSubscribeUserParams{ + ListID: cmd.Value("list-id").(string), + } + return client.Lists.Subscriptions.SubscribeUser( ctx, cmd.Value("user-id").(string), @@ -302,10 +309,6 @@ func handleListsSubscriptionsUnsubscribeUser(ctx context.Context, cmd *cli.Comma return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ListSubscriptionUnsubscribeUserParams{ - ListID: cmd.Value("list-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -317,6 +320,10 @@ func handleListsSubscriptionsUnsubscribeUser(ctx context.Context, cmd *cli.Comma return err } + params := courier.ListSubscriptionUnsubscribeUserParams{ + ListID: cmd.Value("list-id").(string), + } + return client.Lists.Subscriptions.UnsubscribeUser( ctx, cmd.Value("user-id").(string), diff --git a/pkg/cmd/message.go b/pkg/cmd/message.go index e008c9f..1797afb 100644 --- a/pkg/cmd/message.go +++ b/pkg/cmd/message.go @@ -20,8 +20,9 @@ var messagesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "message-id", - Required: true, + Name: "message-id", + Required: true, + PathParam: "message_id", }, }, Action: handleMessagesRetrieve, @@ -114,8 +115,9 @@ var messagesCancel = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "message-id", - Required: true, + Name: "message-id", + Required: true, + PathParam: "message_id", }, }, Action: handleMessagesCancel, @@ -128,8 +130,9 @@ var messagesContent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "message-id", - Required: true, + Name: "message-id", + Required: true, + PathParam: "message_id", }, }, Action: handleMessagesContent, @@ -142,8 +145,9 @@ var messagesHistory = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "message-id", - Required: true, + Name: "message-id", + Required: true, + PathParam: "message_id", }, &requestflag.Flag[*string]{ Name: "type", @@ -205,8 +209,6 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.MessageListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -218,6 +220,8 @@ func handleMessagesList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.MessageListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Messages.List(ctx, params, options...) @@ -333,8 +337,6 @@ func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.MessageHistoryParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -346,6 +348,8 @@ func handleMessagesHistory(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.MessageHistoryParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Messages.History( diff --git a/pkg/cmd/notification.go b/pkg/cmd/notification.go index d8896f0..c6b6c92 100644 --- a/pkg/cmd/notification.go +++ b/pkg/cmd/notification.go @@ -74,8 +74,9 @@ var notificationsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "version", @@ -118,8 +119,9 @@ var notificationsArchive = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleNotificationsArchive, @@ -132,8 +134,9 @@ var notificationsListVersions = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "cursor", @@ -157,8 +160,9 @@ var notificationsPublish = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "version", @@ -176,8 +180,9 @@ var notificationsPutContent = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[map[string]any]{ Name: "content", @@ -214,12 +219,14 @@ var notificationsPutElement = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ - Name: "element-id", - Required: true, + Name: "element-id", + Required: true, + PathParam: "elementId", }, &requestflag.Flag[string]{ Name: "type", @@ -264,12 +271,14 @@ var notificationsPutLocale = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ - Name: "locale-id", - Required: true, + Name: "locale-id", + Required: true, + PathParam: "localeId", }, &requestflag.Flag[[]map[string]any]{ Name: "element", @@ -302,8 +311,9 @@ var notificationsReplace = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[map[string]any]{ Name: "notification", @@ -360,8 +370,9 @@ var notificationsRetrieveContent = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "version", @@ -381,8 +392,6 @@ func handleNotificationsCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -394,6 +403,8 @@ func handleNotificationsCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.NotificationNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.New(ctx, params, options...) @@ -425,8 +436,6 @@ func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationGetParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -438,6 +447,8 @@ func handleNotificationsRetrieve(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.NotificationGetParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.Get( @@ -471,8 +482,6 @@ func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -484,6 +493,8 @@ func handleNotificationsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.NotificationListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.List(ctx, params, options...) @@ -540,8 +551,6 @@ func handleNotificationsListVersions(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationListVersionsParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -553,6 +562,8 @@ func handleNotificationsListVersions(ctx context.Context, cmd *cli.Command) erro return err } + params := courier.NotificationListVersionsParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.ListVersions( @@ -589,8 +600,6 @@ func handleNotificationsPublish(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationPublishParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -602,6 +611,8 @@ func handleNotificationsPublish(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.NotificationPublishParams{} + return client.Notifications.Publish( ctx, cmd.Value("id").(string), @@ -621,8 +632,6 @@ func handleNotificationsPutContent(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationPutContentParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -634,6 +643,8 @@ func handleNotificationsPutContent(ctx context.Context, cmd *cli.Command) error return err } + params := courier.NotificationPutContentParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.PutContent( @@ -670,10 +681,6 @@ func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationPutElementParams{ - ID: cmd.Value("id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -685,6 +692,10 @@ func handleNotificationsPutElement(ctx context.Context, cmd *cli.Command) error return err } + params := courier.NotificationPutElementParams{ + ID: cmd.Value("id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.PutElement( @@ -721,10 +732,6 @@ func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationPutLocaleParams{ - ID: cmd.Value("id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -736,6 +743,10 @@ func handleNotificationsPutLocale(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.NotificationPutLocaleParams{ + ID: cmd.Value("id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.PutLocale( @@ -772,8 +783,6 @@ func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationReplaceParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -785,6 +794,8 @@ func handleNotificationsReplace(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.NotificationReplaceParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.Replace( @@ -821,8 +832,6 @@ func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) e return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationGetContentParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -834,6 +843,8 @@ func handleNotificationsRetrieveContent(ctx context.Context, cmd *cli.Command) e return err } + params := courier.NotificationGetContentParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.GetContent( diff --git a/pkg/cmd/notificationcheck.go b/pkg/cmd/notificationcheck.go index 03ab005..a25e6ff 100644 --- a/pkg/cmd/notificationcheck.go +++ b/pkg/cmd/notificationcheck.go @@ -20,12 +20,14 @@ var notificationsChecksUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ - Name: "submission-id", - Required: true, + Name: "submission-id", + Required: true, + PathParam: "submissionId", }, &requestflag.Flag[[]map[string]any]{ Name: "check", @@ -60,12 +62,14 @@ var notificationsChecksList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ - Name: "submission-id", - Required: true, + Name: "submission-id", + Required: true, + PathParam: "submissionId", }, }, Action: handleNotificationsChecksList, @@ -78,12 +82,14 @@ var notificationsChecksDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ - Name: "submission-id", - Required: true, + Name: "submission-id", + Required: true, + PathParam: "submissionId", }, }, Action: handleNotificationsChecksDelete, @@ -101,10 +107,6 @@ func handleNotificationsChecksUpdate(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationCheckUpdateParams{ - ID: cmd.Value("id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -116,6 +118,10 @@ func handleNotificationsChecksUpdate(ctx context.Context, cmd *cli.Command) erro return err } + params := courier.NotificationCheckUpdateParams{ + ID: cmd.Value("id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.Checks.Update( @@ -152,10 +158,6 @@ func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationCheckListParams{ - ID: cmd.Value("id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -167,6 +169,10 @@ func handleNotificationsChecksList(ctx context.Context, cmd *cli.Command) error return err } + params := courier.NotificationCheckListParams{ + ID: cmd.Value("id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Notifications.Checks.List( @@ -203,10 +209,6 @@ func handleNotificationsChecksDelete(ctx context.Context, cmd *cli.Command) erro return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.NotificationCheckDeleteParams{ - ID: cmd.Value("id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -218,6 +220,10 @@ func handleNotificationsChecksDelete(ctx context.Context, cmd *cli.Command) erro return err } + params := courier.NotificationCheckDeleteParams{ + ID: cmd.Value("id").(string), + } + return client.Notifications.Checks.Delete( ctx, cmd.Value("submission-id").(string), diff --git a/pkg/cmd/profile.go b/pkg/cmd/profile.go index 3704fb8..3b0ee25 100644 --- a/pkg/cmd/profile.go +++ b/pkg/cmd/profile.go @@ -20,8 +20,9 @@ var profilesCreate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[map[string]any]{ Name: "profile", @@ -39,8 +40,9 @@ var profilesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleProfilesRetrieve, @@ -53,8 +55,9 @@ var profilesUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[[]map[string]any]{ Name: "patch", @@ -91,8 +94,9 @@ var profilesDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleProfilesDelete, @@ -105,8 +109,9 @@ var profilesReplace = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[map[string]any]{ Name: "profile", @@ -129,8 +134,6 @@ func handleProfilesCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProfileNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -142,6 +145,8 @@ func handleProfilesCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProfileNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Profiles.New( @@ -220,8 +225,6 @@ func handleProfilesUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProfileUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -233,6 +236,8 @@ func handleProfilesUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProfileUpdateParams{} + return client.Profiles.Update( ctx, cmd.Value("user-id").(string), @@ -277,8 +282,6 @@ func handleProfilesReplace(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProfileReplaceParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -290,6 +293,8 @@ func handleProfilesReplace(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProfileReplaceParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Profiles.Replace( diff --git a/pkg/cmd/profilelist.go b/pkg/cmd/profilelist.go index 48bd1b3..d4ec913 100644 --- a/pkg/cmd/profilelist.go +++ b/pkg/cmd/profilelist.go @@ -20,8 +20,9 @@ var profilesListsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -39,8 +40,9 @@ var profilesListsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleProfilesListsDelete, @@ -53,8 +55,9 @@ var profilesListsSubscribe = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[[]map[string]any]{ Name: "list", @@ -88,8 +91,6 @@ func handleProfilesListsRetrieve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProfileListGetParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -101,6 +102,8 @@ func handleProfilesListsRetrieve(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProfileListGetParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Profiles.Lists.Get( @@ -179,8 +182,6 @@ func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProfileListSubscribeParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -192,6 +193,8 @@ func handleProfilesListsSubscribe(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProfileListSubscribeParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Profiles.Lists.Subscribe( diff --git a/pkg/cmd/provider.go b/pkg/cmd/provider.go index 123c3dc..ca5eddd 100644 --- a/pkg/cmd/provider.go +++ b/pkg/cmd/provider.go @@ -51,8 +51,9 @@ var providersRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleProvidersRetrieve, @@ -65,8 +66,9 @@ var providersUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "provider", @@ -115,8 +117,9 @@ var providersDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleProvidersDelete, @@ -131,8 +134,6 @@ func handleProvidersCreate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProviderNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -144,6 +145,8 @@ func handleProvidersCreate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProviderNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Providers.New(ctx, params, options...) @@ -217,8 +220,6 @@ func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProviderUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -230,6 +231,8 @@ func handleProvidersUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProviderUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Providers.Update( @@ -263,8 +266,6 @@ func handleProvidersList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProviderListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -276,6 +277,8 @@ func handleProvidersList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProviderListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Providers.List(ctx, params, options...) diff --git a/pkg/cmd/providercatalog.go b/pkg/cmd/providercatalog.go index 14ead71..d0283e8 100644 --- a/pkg/cmd/providercatalog.go +++ b/pkg/cmd/providercatalog.go @@ -47,8 +47,6 @@ func handleProvidersCatalogList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.ProviderCatalogListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -60,6 +58,8 @@ func handleProvidersCatalogList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.ProviderCatalogListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Providers.Catalog.List(ctx, params, options...) diff --git a/pkg/cmd/request.go b/pkg/cmd/request.go index ff00256..723e450 100644 --- a/pkg/cmd/request.go +++ b/pkg/cmd/request.go @@ -18,8 +18,9 @@ var requestsArchive = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "request-id", - Required: true, + Name: "request-id", + Required: true, + PathParam: "request_id", }, }, Action: handleRequestsArchive, diff --git a/pkg/cmd/routingstrategy.go b/pkg/cmd/routingstrategy.go index 3447e2f..69dc848 100644 --- a/pkg/cmd/routingstrategy.go +++ b/pkg/cmd/routingstrategy.go @@ -71,8 +71,9 @@ var routingStrategiesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleRoutingStrategiesRetrieve, @@ -106,8 +107,9 @@ var routingStrategiesArchive = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, }, Action: handleRoutingStrategiesArchive, @@ -120,8 +122,9 @@ var routingStrategiesListNotifications = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -145,8 +148,9 @@ var routingStrategiesReplace = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "id", - Required: true, + Name: "id", + Required: true, + PathParam: "id", }, &requestflag.Flag[string]{ Name: "name", @@ -202,8 +206,6 @@ func handleRoutingStrategiesCreate(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.RoutingStrategyNewParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -215,6 +217,8 @@ func handleRoutingStrategiesCreate(ctx context.Context, cmd *cli.Command) error return err } + params := courier.RoutingStrategyNewParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.RoutingStrategies.New(ctx, params, options...) @@ -285,8 +289,6 @@ func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.RoutingStrategyListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -298,6 +300,8 @@ func handleRoutingStrategiesList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.RoutingStrategyListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.RoutingStrategies.List(ctx, params, options...) @@ -354,8 +358,6 @@ func handleRoutingStrategiesListNotifications(ctx context.Context, cmd *cli.Comm return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.RoutingStrategyListNotificationsParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -367,6 +369,8 @@ func handleRoutingStrategiesListNotifications(ctx context.Context, cmd *cli.Comm return err } + params := courier.RoutingStrategyListNotificationsParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.RoutingStrategies.ListNotifications( @@ -403,8 +407,6 @@ func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.RoutingStrategyReplaceParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -416,6 +418,8 @@ func handleRoutingStrategiesReplace(ctx context.Context, cmd *cli.Command) error return err } + params := courier.RoutingStrategyReplaceParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.RoutingStrategies.Replace( diff --git a/pkg/cmd/send.go b/pkg/cmd/send.go index 14bc54a..c11da4a 100644 --- a/pkg/cmd/send.go +++ b/pkg/cmd/send.go @@ -100,8 +100,6 @@ func handleSendMessage(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.SendMessageParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -113,6 +111,8 @@ func handleSendMessage(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.SendMessageParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Send.Message(ctx, params, options...) diff --git a/pkg/cmd/tenant.go b/pkg/cmd/tenant.go index 8930950..2d19db9 100644 --- a/pkg/cmd/tenant.go +++ b/pkg/cmd/tenant.go @@ -20,8 +20,9 @@ var tenantsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, }, Action: handleTenantsRetrieve, @@ -34,8 +35,9 @@ var tenantsUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ Name: "name", @@ -110,8 +112,9 @@ var tenantsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, }, Action: handleTenantsDelete, @@ -124,8 +127,9 @@ var tenantsListUsers = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -195,8 +199,6 @@ func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantUpdateParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -208,6 +210,8 @@ func handleTenantsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.TenantUpdateParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.Update( @@ -241,8 +245,6 @@ func handleTenantsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -254,6 +256,8 @@ func handleTenantsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.TenantListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.List(ctx, params, options...) @@ -310,8 +314,6 @@ func handleTenantsListUsers(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantListUsersParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -323,6 +325,8 @@ func handleTenantsListUsers(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.TenantListUsersParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.ListUsers( diff --git a/pkg/cmd/tenantpreferenceitem.go b/pkg/cmd/tenantpreferenceitem.go index c19b074..1be7117 100644 --- a/pkg/cmd/tenantpreferenceitem.go +++ b/pkg/cmd/tenantpreferenceitem.go @@ -18,12 +18,14 @@ var tenantsPreferencesItemsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ - Name: "topic-id", - Required: true, + Name: "topic-id", + Required: true, + PathParam: "topic_id", }, &requestflag.Flag[string]{ Name: "status", @@ -52,12 +54,14 @@ var tenantsPreferencesItemsDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ - Name: "topic-id", - Required: true, + Name: "topic-id", + Required: true, + PathParam: "topic_id", }, }, Action: handleTenantsPreferencesItemsDelete, @@ -75,10 +79,6 @@ func handleTenantsPreferencesItemsUpdate(ctx context.Context, cmd *cli.Command) return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantPreferenceItemUpdateParams{ - TenantID: cmd.Value("tenant-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -90,6 +90,10 @@ func handleTenantsPreferencesItemsUpdate(ctx context.Context, cmd *cli.Command) return err } + params := courier.TenantPreferenceItemUpdateParams{ + TenantID: cmd.Value("tenant-id").(string), + } + return client.Tenants.Preferences.Items.Update( ctx, cmd.Value("topic-id").(string), @@ -109,10 +113,6 @@ func handleTenantsPreferencesItemsDelete(ctx context.Context, cmd *cli.Command) return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantPreferenceItemDeleteParams{ - TenantID: cmd.Value("tenant-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -124,6 +124,10 @@ func handleTenantsPreferencesItemsDelete(ctx context.Context, cmd *cli.Command) return err } + params := courier.TenantPreferenceItemDeleteParams{ + TenantID: cmd.Value("tenant-id").(string), + } + return client.Tenants.Preferences.Items.Delete( ctx, cmd.Value("topic-id").(string), diff --git a/pkg/cmd/tenanttemplate.go b/pkg/cmd/tenanttemplate.go index 0338e55..de615db 100644 --- a/pkg/cmd/tenanttemplate.go +++ b/pkg/cmd/tenanttemplate.go @@ -20,12 +20,14 @@ var tenantsTemplatesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "template_id", }, }, Action: handleTenantsTemplatesRetrieve, @@ -38,8 +40,9 @@ var tenantsTemplatesList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -62,12 +65,14 @@ var tenantsTemplatesPublish = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "template_id", }, &requestflag.Flag[string]{ Name: "version", @@ -86,12 +91,14 @@ var tenantsTemplatesReplace = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "template_id", }, &requestflag.Flag[map[string]any]{ Name: "template", @@ -140,10 +147,6 @@ func handleTenantsTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantTemplateGetParams{ - TenantID: cmd.Value("tenant-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -155,6 +158,10 @@ func handleTenantsTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error return err } + params := courier.TenantTemplateGetParams{ + TenantID: cmd.Value("tenant-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.Templates.Get( @@ -191,8 +198,6 @@ func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantTemplateListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -204,6 +209,8 @@ func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.TenantTemplateListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.Templates.List( @@ -240,10 +247,6 @@ func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantTemplatePublishParams{ - TenantID: cmd.Value("tenant-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -255,6 +258,10 @@ func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error return err } + params := courier.TenantTemplatePublishParams{ + TenantID: cmd.Value("tenant-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.Templates.Publish( @@ -291,10 +298,6 @@ func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantTemplateReplaceParams{ - TenantID: cmd.Value("tenant-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -306,6 +309,10 @@ func handleTenantsTemplatesReplace(ctx context.Context, cmd *cli.Command) error return err } + params := courier.TenantTemplateReplaceParams{ + TenantID: cmd.Value("tenant-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.Templates.Replace( diff --git a/pkg/cmd/tenanttemplateversion.go b/pkg/cmd/tenanttemplateversion.go index b65cbb1..2002b98 100644 --- a/pkg/cmd/tenanttemplateversion.go +++ b/pkg/cmd/tenanttemplateversion.go @@ -20,16 +20,19 @@ var tenantsTemplatesVersionsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[string]{ - Name: "template-id", - Required: true, + Name: "template-id", + Required: true, + PathParam: "template_id", }, &requestflag.Flag[string]{ - Name: "version", - Required: true, + Name: "version", + Required: true, + PathParam: "version", }, }, Action: handleTenantsTemplatesVersionsRetrieve, @@ -47,11 +50,6 @@ func handleTenantsTemplatesVersionsRetrieve(ctx context.Context, cmd *cli.Comman return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TenantTemplateVersionGetParams{ - TenantID: cmd.Value("tenant-id").(string), - TemplateID: cmd.Value("template-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -63,6 +61,11 @@ func handleTenantsTemplatesVersionsRetrieve(ctx context.Context, cmd *cli.Comman return err } + params := courier.TenantTemplateVersionGetParams{ + TenantID: cmd.Value("tenant-id").(string), + TemplateID: cmd.Value("template-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Tenants.Templates.Versions.Get( diff --git a/pkg/cmd/translation.go b/pkg/cmd/translation.go index 70b34bd..eda1899 100644 --- a/pkg/cmd/translation.go +++ b/pkg/cmd/translation.go @@ -20,12 +20,14 @@ var translationsRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "domain", - Required: true, + Name: "domain", + Required: true, + PathParam: "domain", }, &requestflag.Flag[string]{ - Name: "locale", - Required: true, + Name: "locale", + Required: true, + PathParam: "locale", }, }, Action: handleTranslationsRetrieve, @@ -38,12 +40,14 @@ var translationsUpdate = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "domain", - Required: true, + Name: "domain", + Required: true, + PathParam: "domain", }, &requestflag.Flag[string]{ - Name: "locale", - Required: true, + Name: "locale", + Required: true, + PathParam: "locale", }, &requestflag.Flag[string]{ Name: "body", @@ -66,10 +70,6 @@ func handleTranslationsRetrieve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TranslationGetParams{ - Domain: cmd.Value("domain").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -81,6 +81,10 @@ func handleTranslationsRetrieve(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.TranslationGetParams{ + Domain: cmd.Value("domain").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Translations.Get( @@ -117,10 +121,6 @@ func handleTranslationsUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.TranslationUpdateParams{ - Domain: cmd.Value("domain").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -132,6 +132,10 @@ func handleTranslationsUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.TranslationUpdateParams{ + Domain: cmd.Value("domain").(string), + } + return client.Translations.Update( ctx, cmd.Value("locale").(string), diff --git a/pkg/cmd/userpreference.go b/pkg/cmd/userpreference.go index 0c6ff3d..9965737 100644 --- a/pkg/cmd/userpreference.go +++ b/pkg/cmd/userpreference.go @@ -20,8 +20,9 @@ var usersPreferencesRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[*string]{ Name: "tenant-id", @@ -39,12 +40,14 @@ var usersPreferencesRetrieveTopic = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "topic-id", - Required: true, + Name: "topic-id", + Required: true, + PathParam: "topic_id", }, &requestflag.Flag[*string]{ Name: "tenant-id", @@ -62,12 +65,14 @@ var usersPreferencesUpdateOrCreateTopic = requestflag.WithInnerFlags(cli.Command Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "topic-id", - Required: true, + Name: "topic-id", + Required: true, + PathParam: "topic_id", }, &requestflag.Flag[map[string]any]{ Name: "topic", @@ -112,8 +117,6 @@ func handleUsersPreferencesRetrieve(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserPreferenceGetParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -125,6 +128,8 @@ func handleUsersPreferencesRetrieve(ctx context.Context, cmd *cli.Command) error return err } + params := courier.UserPreferenceGetParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Users.Preferences.Get( @@ -161,10 +166,6 @@ func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserPreferenceGetTopicParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -176,6 +177,10 @@ func handleUsersPreferencesRetrieveTopic(ctx context.Context, cmd *cli.Command) return err } + params := courier.UserPreferenceGetTopicParams{ + UserID: cmd.Value("user-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Users.Preferences.GetTopic( @@ -212,10 +217,6 @@ func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Com return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserPreferenceUpdateOrNewTopicParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -227,6 +228,10 @@ func handleUsersPreferencesUpdateOrCreateTopic(ctx context.Context, cmd *cli.Com return err } + params := courier.UserPreferenceUpdateOrNewTopicParams{ + UserID: cmd.Value("user-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Users.Preferences.UpdateOrNewTopic( diff --git a/pkg/cmd/usertenant.go b/pkg/cmd/usertenant.go index a2d830a..b4c8ee6 100644 --- a/pkg/cmd/usertenant.go +++ b/pkg/cmd/usertenant.go @@ -20,8 +20,9 @@ var usersTenantsList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[*string]{ Name: "cursor", @@ -44,8 +45,9 @@ var usersTenantsAddMultiple = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[[]map[string]any]{ Name: "tenant", @@ -86,12 +88,14 @@ var usersTenantsAddSingle = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, &requestflag.Flag[map[string]any]{ Name: "profile", @@ -108,8 +112,9 @@ var usersTenantsRemoveAll = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleUsersTenantsRemoveAll, @@ -122,12 +127,14 @@ var usersTenantsRemoveSingle = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "tenant-id", - Required: true, + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", }, }, Action: handleUsersTenantsRemoveSingle, @@ -145,8 +152,6 @@ func handleUsersTenantsList(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTenantListParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -158,6 +163,8 @@ func handleUsersTenantsList(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.UserTenantListParams{} + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Users.Tenants.List( @@ -194,8 +201,6 @@ func handleUsersTenantsAddMultiple(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTenantAddMultipleParams{} - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -207,6 +212,8 @@ func handleUsersTenantsAddMultiple(ctx context.Context, cmd *cli.Command) error return err } + params := courier.UserTenantAddMultipleParams{} + return client.Users.Tenants.AddMultiple( ctx, cmd.Value("user-id").(string), @@ -226,10 +233,6 @@ func handleUsersTenantsAddSingle(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTenantAddSingleParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -241,6 +244,10 @@ func handleUsersTenantsAddSingle(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.UserTenantAddSingleParams{ + UserID: cmd.Value("user-id").(string), + } + return client.Users.Tenants.AddSingle( ctx, cmd.Value("tenant-id").(string), @@ -285,10 +292,6 @@ func handleUsersTenantsRemoveSingle(ctx context.Context, cmd *cli.Command) error return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTenantRemoveSingleParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -300,6 +303,10 @@ func handleUsersTenantsRemoveSingle(ctx context.Context, cmd *cli.Command) error return err } + params := courier.UserTenantRemoveSingleParams{ + UserID: cmd.Value("user-id").(string), + } + return client.Users.Tenants.RemoveSingle( ctx, cmd.Value("tenant-id").(string), diff --git a/pkg/cmd/usertoken.go b/pkg/cmd/usertoken.go index acb0b94..3ac0d84 100644 --- a/pkg/cmd/usertoken.go +++ b/pkg/cmd/usertoken.go @@ -20,12 +20,14 @@ var usersTokensRetrieve = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "token", - Required: true, + Name: "token", + Required: true, + PathParam: "token", }, }, Action: handleUsersTokensRetrieve, @@ -38,12 +40,14 @@ var usersTokensUpdate = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "token", - Required: true, + Name: "token", + Required: true, + PathParam: "token", }, &requestflag.Flag[[]map[string]any]{ Name: "patch", @@ -79,8 +83,9 @@ var usersTokensList = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleUsersTokensList, @@ -93,12 +98,14 @@ var usersTokensDelete = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "token", - Required: true, + Name: "token", + Required: true, + PathParam: "token", }, }, Action: handleUsersTokensDelete, @@ -111,8 +118,9 @@ var usersTokensAddMultiple = cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, }, Action: handleUsersTokensAddMultiple, @@ -125,12 +133,14 @@ var usersTokensAddSingle = requestflag.WithInnerFlags(cli.Command{ Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ - Name: "user-id", - Required: true, + Name: "user-id", + Required: true, + PathParam: "user_id", }, &requestflag.Flag[string]{ - Name: "token", - Required: true, + Name: "token", + Required: true, + PathParam: "token", }, &requestflag.Flag[string]{ Name: "provider-key", @@ -229,10 +239,6 @@ func handleUsersTokensRetrieve(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTokenGetParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -244,6 +250,10 @@ func handleUsersTokensRetrieve(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.UserTokenGetParams{ + UserID: cmd.Value("user-id").(string), + } + var res []byte options = append(options, option.WithResponseBodyInto(&res)) _, err = client.Users.Tokens.Get( @@ -280,10 +290,6 @@ func handleUsersTokensUpdate(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTokenUpdateParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -295,6 +301,10 @@ func handleUsersTokensUpdate(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.UserTokenUpdateParams{ + UserID: cmd.Value("user-id").(string), + } + return client.Users.Tokens.Update( ctx, cmd.Value("token").(string), @@ -356,10 +366,6 @@ func handleUsersTokensDelete(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTokenDeleteParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -371,6 +377,10 @@ func handleUsersTokensDelete(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.UserTokenDeleteParams{ + UserID: cmd.Value("user-id").(string), + } + return client.Users.Tokens.Delete( ctx, cmd.Value("token").(string), @@ -415,10 +425,6 @@ func handleUsersTokensAddSingle(ctx context.Context, cmd *cli.Command) error { return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) } - params := courier.UserTokenAddSingleParams{ - UserID: cmd.Value("user-id").(string), - } - options, err := flagOptions( cmd, apiquery.NestedQueryFormatBrackets, @@ -430,6 +436,10 @@ func handleUsersTokensAddSingle(ctx context.Context, cmd *cli.Command) error { return err } + params := courier.UserTokenAddSingleParams{ + UserID: cmd.Value("user-id").(string), + } + return client.Users.Tokens.AddSingle( ctx, cmd.Value("token").(string), From f8c13742dc6f28cd37ce661ea259df7de7889dbf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 02:24:10 +0000 Subject: [PATCH 18/29] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 7149859..a59050a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-3f78581b4e078a1f620d9f587f18d77bcde6d20f56b0e4ae798648f4236494fb.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-2fca943954773292b6ca82dcb420c6500d118d2fbbe1cd4718afcb804f6c818c.yml openapi_spec_hash: 6bd33e0396d85e11bb46f0d549af93a3 config_hash: afcc4f6f8c33ca3f338589e32e086f56 From 26b96175c773dae96c8eb7a877126c309f71040f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 02:15:08 +0000 Subject: [PATCH 19/29] chore: redact api-key headers in debug logs --- internal/debugmiddleware/debug_middleware.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/debugmiddleware/debug_middleware.go b/internal/debugmiddleware/debug_middleware.go index f07b93b..647f1de 100644 --- a/internal/debugmiddleware/debug_middleware.go +++ b/internal/debugmiddleware/debug_middleware.go @@ -21,7 +21,12 @@ const redactedPlaceholder = "" // Headers known to contain sensitive information like an API key. Note that this exclude `Authorization`, // which is handled specially in `redactRequest` below. -var sensitiveHeaders = []string{} +var sensitiveHeaders = []string{ + "api-key", + "x-api-key", + "cookie", + "set-cookie", +} // RequestLogger is a middleware that logs HTTP requests and responses. type RequestLogger struct { From 0edc6958240e6bec5c0403c72149a1c2a8bbf4e4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:06:36 +0000 Subject: [PATCH 20/29] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. --- .github/actions/setup-go/action.yml | 4 ++-- .github/workflows/ci.yml | 10 +++++----- .github/workflows/publish-release.yml | 6 +++--- .github/workflows/release-doctor.yml | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/actions/setup-go/action.yml b/.github/actions/setup-go/action.yml index f82d2ed..2f43bae 100644 --- a/.github/actions/setup-go/action.yml +++ b/.github/actions/setup-go/action.yml @@ -7,7 +7,7 @@ inputs: runs: using: composite steps: - - uses: stainless-api/retrieve-github-access-token@v1 + - uses: stainless-api/retrieve-github-access-token@1f03f929b746c5b03dcdafa2bebbb18ca5672e1a # v1.0.0 if: github.repository == 'stainless-sdks/courier-cli' id: get_token with: @@ -20,7 +20,7 @@ runs: run: git config --global url."https://x-access-token:${{ steps.get_token.outputs.github_access_token }}@github.com/stainless-sdks/courier-go".insteadOf "https://github.com/stainless-sdks/courier-go" - name: Setup go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: ./go.mod diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1aa995..e2ee0a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-go with: @@ -51,7 +51,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/courier-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-go with: @@ -66,7 +66,7 @@ jobs: run: ./scripts/bootstrap - name: Run goreleaser - uses: goreleaser/goreleaser-action@v6.1.0 + uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 with: version: latest args: release --snapshot --clean --skip=publish @@ -78,7 +78,7 @@ jobs: github.repository == 'stainless-sdks/courier-cli' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -98,7 +98,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/courier-cli' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-go with: diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 86f6889..267bb2b 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -16,15 +16,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: "go.mod" - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6.1.0 + uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 with: version: latest args: release --clean diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 423c57f..ffec563 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'trycourier/courier-cli' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | From f4afb0962c0d224f65d5c3d2061629787ada86b1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 23:55:20 +0000 Subject: [PATCH 21/29] feat(api): add journeys methods and journeys:templates resource --- .stats.yml | 8 +- pkg/cmd/cmd.go | 20 ++ pkg/cmd/journey.go | 386 ++++++++++++++++++++++ pkg/cmd/journey_test.go | 146 +++++++++ pkg/cmd/journeytemplate.go | 549 ++++++++++++++++++++++++++++++++ pkg/cmd/journeytemplate_test.go | 224 +++++++++++++ 6 files changed, 1329 insertions(+), 4 deletions(-) create mode 100644 pkg/cmd/journeytemplate.go create mode 100644 pkg/cmd/journeytemplate_test.go diff --git a/.stats.yml b/.stats.yml index a59050a..eb39c6b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-2fca943954773292b6ca82dcb420c6500d118d2fbbe1cd4718afcb804f6c818c.yml -openapi_spec_hash: 6bd33e0396d85e11bb46f0d549af93a3 -config_hash: afcc4f6f8c33ca3f338589e32e086f56 +configured_endpoints: 116 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-389afcb57163d4391f72d240c33a82d0522b958400603f137981480cda54558a.yml +openapi_spec_hash: c861d7fcd81ad5bfdaf8da0f5a84f497 +config_hash: e40fec72c1ab30fded57c222ace689b5 diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index bd4f45a..42d7a85 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -163,8 +163,28 @@ func init() { Category: "API RESOURCE", Suggest: true, Commands: []*cli.Command{ + &journeysCreate, + &journeysRetrieve, &journeysList, + &journeysArchive, &journeysInvoke, + &journeysListVersions, + &journeysPublish, + &journeysReplace, + }, + }, + { + Name: "journeys:templates", + Category: "API RESOURCE", + Suggest: true, + Commands: []*cli.Command{ + &journeysTemplatesCreate, + &journeysTemplatesRetrieve, + &journeysTemplatesList, + &journeysTemplatesArchive, + &journeysTemplatesListVersions, + &journeysTemplatesPublish, + &journeysTemplatesReplace, }, }, { diff --git a/pkg/cmd/journey.go b/pkg/cmd/journey.go index f270cc1..cc4c933 100644 --- a/pkg/cmd/journey.go +++ b/pkg/cmd/journey.go @@ -14,6 +14,54 @@ import ( "github.com/urfave/cli/v3" ) +var journeysCreate = cli.Command{ + Name: "create", + Usage: "Create a new journey. The journey is created in DRAFT state. Use POST\n/journeys/{templateId}/publish to make it live.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "name", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[[]map[string]any]{ + Name: "node", + Required: true, + BodyPath: "nodes", + }, + &requestflag.Flag[bool]{ + Name: "enabled", + BodyPath: "enabled", + }, + &requestflag.Flag[string]{ + Name: "state", + Usage: `Allowed values: "DRAFT", "PUBLISHED".`, + BodyPath: "state", + }, + }, + Action: handleJourneysCreate, + HideHelpCommand: true, +} + +var journeysRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Fetch a journey by id. Pass `?version=draft` (default `published`) to retrieve\nthe working draft, or `?version=vN` to retrieve a historical version.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "version", + QueryPath: "version", + }, + }, + Action: handleJourneysRetrieve, + HideHelpCommand: true, +} + var journeysList = cli.Command{ Name: "list", Usage: "Get the list of journeys.", @@ -35,6 +83,21 @@ var journeysList = cli.Command{ HideHelpCommand: true, } +var journeysArchive = cli.Command{ + Name: "archive", + Usage: "Archive a journey. Archived journeys cannot be invoked. Existing journey runs\ncontinue to completion.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + }, + Action: handleJourneysArchive, + HideHelpCommand: true, +} + var journeysInvoke = cli.Command{ Name: "invoke", Usage: "Invoke a journey run from a journey template.", @@ -65,6 +128,164 @@ var journeysInvoke = cli.Command{ HideHelpCommand: true, } +var journeysListVersions = cli.Command{ + Name: "list-versions", + Usage: "List published versions of a journey, ordered most recent first.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + }, + Action: handleJourneysListVersions, + HideHelpCommand: true, +} + +var journeysPublish = cli.Command{ + Name: "publish", + Usage: "Publish the current draft as a new version. Optionally rollback to a prior\nversion by passing `{ version: 'vN' }`.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "version", + BodyPath: "version", + }, + }, + Action: handleJourneysPublish, + HideHelpCommand: true, +} + +var journeysReplace = cli.Command{ + Name: "replace", + Usage: "Replace the journey draft. Updates the working draft only; call POST\n/journeys/{templateId}/publish to make it live.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "name", + Required: true, + BodyPath: "name", + }, + &requestflag.Flag[[]map[string]any]{ + Name: "node", + Required: true, + BodyPath: "nodes", + }, + &requestflag.Flag[bool]{ + Name: "enabled", + BodyPath: "enabled", + }, + &requestflag.Flag[string]{ + Name: "state", + Usage: `Allowed values: "DRAFT", "PUBLISHED".`, + BodyPath: "state", + }, + }, + Action: handleJourneysReplace, + HideHelpCommand: true, +} + +func handleJourneysCreate(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.New(ctx, params, options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys create", + Transform: transform, + }) +} + +func handleJourneysRetrieve(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyGetParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Get( + ctx, + cmd.Value("template-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys retrieve", + Transform: transform, + }) +} + func handleJourneysList(ctx context.Context, cmd *cli.Command) error { client := courier.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -106,6 +327,31 @@ func handleJourneysList(ctx context.Context, cmd *cli.Command) error { }) } +func handleJourneysArchive(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + return client.Journeys.Archive(ctx, cmd.Value("template-id").(string), options...) +} + func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { client := courier.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() @@ -154,3 +400,143 @@ func handleJourneysInvoke(ctx context.Context, cmd *cli.Command) error { Transform: transform, }) } + +func handleJourneysListVersions(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.ListVersions(ctx, cmd.Value("template-id").(string), options...) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys list-versions", + Transform: transform, + }) +} + +func handleJourneysPublish(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyPublishParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Publish( + ctx, + cmd.Value("template-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys publish", + Transform: transform, + }) +} + +func handleJourneysReplace(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyReplaceParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Replace( + ctx, + cmd.Value("template-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys replace", + Transform: transform, + }) +} diff --git a/pkg/cmd/journey_test.go b/pkg/cmd/journey_test.go index 7363d74..b04f235 100644 --- a/pkg/cmd/journey_test.go +++ b/pkg/cmd/journey_test.go @@ -8,6 +8,65 @@ import ( "github.com/trycourier/courier-cli/v3/internal/mocktest" ) +func TestJourneysCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys", "create", + "--name", "Welcome Journey", + "--node", "{trigger_type: api-invoke, type: trigger, id: trigger-1, conditions: [string, string], schema: {foo: bar}}", + "--node", "{trigger_type: api-invoke, type: trigger, id: send-1, conditions: [string, string], schema: {foo: bar}}", + "--enabled=true", + "--state", "DRAFT", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "name: Welcome Journey\n" + + "nodes:\n" + + " - trigger_type: api-invoke\n" + + " type: trigger\n" + + " id: trigger-1\n" + + " conditions:\n" + + " - string\n" + + " - string\n" + + " schema:\n" + + " foo: bar\n" + + " - trigger_type: api-invoke\n" + + " type: trigger\n" + + " id: send-1\n" + + " conditions:\n" + + " - string\n" + + " - string\n" + + " schema:\n" + + " foo: bar\n" + + "enabled: true\n" + + "state: DRAFT\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "journeys", "create", + ) + }) +} + +func TestJourneysRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys", "retrieve", + "--template-id", "x", + "--version", "published", + ) + }) +} + func TestJourneysList(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { @@ -21,6 +80,18 @@ func TestJourneysList(t *testing.T) { }) } +func TestJourneysArchive(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys", "archive", + "--template-id", "x", + ) + }) +} + func TestJourneysInvoke(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { @@ -52,3 +123,78 @@ func TestJourneysInvoke(t *testing.T) { ) }) } + +func TestJourneysListVersions(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys", "list-versions", + "--template-id", "x", + ) + }) +} + +func TestJourneysPublish(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys", "publish", + "--template-id", "x", + "--version", "v321669910225", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("version: v321669910225") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "journeys", "publish", + "--template-id", "x", + ) + }) +} + +func TestJourneysReplace(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys", "replace", + "--template-id", "x", + "--name", "Welcome Journey v2", + "--node", "{trigger_type: api-invoke, type: trigger, id: x, conditions: [string, string], schema: {foo: bar}}", + "--enabled=true", + "--state", "DRAFT", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "name: Welcome Journey v2\n" + + "nodes:\n" + + " - trigger_type: api-invoke\n" + + " type: trigger\n" + + " id: x\n" + + " conditions:\n" + + " - string\n" + + " - string\n" + + " schema:\n" + + " foo: bar\n" + + "enabled: true\n" + + "state: DRAFT\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "journeys", "replace", + "--template-id", "x", + ) + }) +} diff --git a/pkg/cmd/journeytemplate.go b/pkg/cmd/journeytemplate.go new file mode 100644 index 0000000..1ec8e96 --- /dev/null +++ b/pkg/cmd/journeytemplate.go @@ -0,0 +1,549 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "context" + "fmt" + + "github.com/tidwall/gjson" + "github.com/trycourier/courier-cli/v3/internal/apiquery" + "github.com/trycourier/courier-cli/v3/internal/requestflag" + "github.com/trycourier/courier-go/v4" + "github.com/trycourier/courier-go/v4/option" + "github.com/urfave/cli/v3" +) + +var journeysTemplatesCreate = requestflag.WithInnerFlags(cli.Command{ + Name: "create", + Usage: "Create a notification template scoped to this journey. The template is created\nin DRAFT state.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "channel", + Required: true, + BodyPath: "channel", + }, + &requestflag.Flag[map[string]any]{ + Name: "notification", + Required: true, + BodyPath: "notification", + }, + &requestflag.Flag[string]{ + Name: "provider-key", + BodyPath: "providerKey", + }, + &requestflag.Flag[string]{ + Name: "state", + BodyPath: "state", + }, + }, + Action: handleJourneysTemplatesCreate, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "notification": { + &requestflag.InnerFlag[map[string]any]{ + Name: "notification.brand", + InnerField: "brand", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "notification.content", + InnerField: "content", + }, + &requestflag.InnerFlag[string]{ + Name: "notification.name", + InnerField: "name", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "notification.subscription", + InnerField: "subscription", + }, + &requestflag.InnerFlag[[]string]{ + Name: "notification.tags", + InnerField: "tags", + }, + }, +}) + +var journeysTemplatesRetrieve = cli.Command{ + Name: "retrieve", + Usage: "Fetch a journey-scoped notification template by id. Pass `?version=draft`\n(default `published`) to retrieve the working draft, or `?version=vN` for a\nhistorical version.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "notification-id", + Required: true, + PathParam: "notificationId", + }, + }, + Action: handleJourneysTemplatesRetrieve, + HideHelpCommand: true, +} + +var journeysTemplatesList = cli.Command{ + Name: "list", + Usage: "List notification templates scoped to this journey. Templates scoped to a\njourney can only be referenced from `send` nodes of the same journey.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "cursor", + QueryPath: "cursor", + }, + &requestflag.Flag[int64]{ + Name: "limit", + QueryPath: "limit", + }, + }, + Action: handleJourneysTemplatesList, + HideHelpCommand: true, +} + +var journeysTemplatesArchive = cli.Command{ + Name: "archive", + Usage: "Archive a journey-scoped notification template. Archived templates cannot be\nsent.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "notification-id", + Required: true, + PathParam: "notificationId", + }, + }, + Action: handleJourneysTemplatesArchive, + HideHelpCommand: true, +} + +var journeysTemplatesListVersions = cli.Command{ + Name: "list-versions", + Usage: "List published versions of a journey-scoped notification template, ordered most\nrecent first.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "notification-id", + Required: true, + PathParam: "notificationId", + }, + }, + Action: handleJourneysTemplatesListVersions, + HideHelpCommand: true, +} + +var journeysTemplatesPublish = cli.Command{ + Name: "publish", + Usage: "Publish the current draft of a journey-scoped notification template.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "notification-id", + Required: true, + PathParam: "notificationId", + }, + &requestflag.Flag[string]{ + Name: "version", + BodyPath: "version", + }, + }, + Action: handleJourneysTemplatesPublish, + HideHelpCommand: true, +} + +var journeysTemplatesReplace = requestflag.WithInnerFlags(cli.Command{ + Name: "replace", + Usage: "Replace a journey-scoped notification template draft.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "templateId", + }, + &requestflag.Flag[string]{ + Name: "notification-id", + Required: true, + PathParam: "notificationId", + }, + &requestflag.Flag[map[string]any]{ + Name: "notification", + Required: true, + BodyPath: "notification", + }, + &requestflag.Flag[string]{ + Name: "state", + BodyPath: "state", + }, + }, + Action: handleJourneysTemplatesReplace, + HideHelpCommand: true, +}, map[string][]requestflag.HasOuterFlag{ + "notification": { + &requestflag.InnerFlag[map[string]any]{ + Name: "notification.brand", + InnerField: "brand", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "notification.content", + InnerField: "content", + }, + &requestflag.InnerFlag[string]{ + Name: "notification.name", + InnerField: "name", + }, + &requestflag.InnerFlag[map[string]any]{ + Name: "notification.subscription", + InnerField: "subscription", + }, + &requestflag.InnerFlag[[]string]{ + Name: "notification.tags", + InnerField: "tags", + }, + }, +}) + +func handleJourneysTemplatesCreate(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplateNewParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Templates.New( + ctx, + cmd.Value("template-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys:templates create", + Transform: transform, + }) +} + +func handleJourneysTemplatesRetrieve(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("notification-id") && len(unusedArgs) > 0 { + cmd.Set("notification-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplateGetParams{ + TemplateID: cmd.Value("template-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Templates.Get( + ctx, + cmd.Value("notification-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys:templates retrieve", + Transform: transform, + }) +} + +func handleJourneysTemplatesList(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplateListParams{} + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Templates.List( + ctx, + cmd.Value("template-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys:templates list", + Transform: transform, + }) +} + +func handleJourneysTemplatesArchive(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("notification-id") && len(unusedArgs) > 0 { + cmd.Set("notification-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplateArchiveParams{ + TemplateID: cmd.Value("template-id").(string), + } + + return client.Journeys.Templates.Archive( + ctx, + cmd.Value("notification-id").(string), + params, + options..., + ) +} + +func handleJourneysTemplatesListVersions(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("notification-id") && len(unusedArgs) > 0 { + cmd.Set("notification-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplateListVersionsParams{ + TemplateID: cmd.Value("template-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Templates.ListVersions( + ctx, + cmd.Value("notification-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys:templates list-versions", + Transform: transform, + }) +} + +func handleJourneysTemplatesPublish(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("notification-id") && len(unusedArgs) > 0 { + cmd.Set("notification-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplatePublishParams{ + TemplateID: cmd.Value("template-id").(string), + } + + return client.Journeys.Templates.Publish( + ctx, + cmd.Value("notification-id").(string), + params, + options..., + ) +} + +func handleJourneysTemplatesReplace(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("notification-id") && len(unusedArgs) > 0 { + cmd.Set("notification-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + ApplicationJSON, + false, + ) + if err != nil { + return err + } + + params := courier.JourneyTemplateReplaceParams{ + TemplateID: cmd.Value("template-id").(string), + } + + var res []byte + options = append(options, option.WithResponseBodyInto(&res)) + _, err = client.Journeys.Templates.Replace( + ctx, + cmd.Value("notification-id").(string), + params, + options..., + ) + if err != nil { + return err + } + + obj := gjson.ParseBytes(res) + format := cmd.Root().String("format") + explicitFormat := cmd.Root().IsSet("format") + transform := cmd.Root().String("transform") + return ShowJSON(obj, ShowJSONOpts{ + ExplicitFormat: explicitFormat, + Format: format, + RawOutput: cmd.Root().Bool("raw-output"), + Title: "journeys:templates replace", + Transform: transform, + }) +} diff --git a/pkg/cmd/journeytemplate_test.go b/pkg/cmd/journeytemplate_test.go new file mode 100644 index 0000000..67b11c8 --- /dev/null +++ b/pkg/cmd/journeytemplate_test.go @@ -0,0 +1,224 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package cmd + +import ( + "testing" + + "github.com/trycourier/courier-cli/v3/internal/mocktest" + "github.com/trycourier/courier-cli/v3/internal/requestflag" +) + +func TestJourneysTemplatesCreate(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "create", + "--template-id", "x", + "--channel", "email", + "--notification", "{brand: {id: id}, content: {elements: [{channels: [string], if: if, loop: loop, ref: ref, type: text}], version: '2022-01-01', scope: default}, name: Welcome email, subscription: {topic_id: topic_id}, tags: [string]}", + "--provider-key", "x", + "--state", "state", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(journeysTemplatesCreate) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "create", + "--template-id", "x", + "--channel", "email", + "--notification.brand", "{id: id}", + "--notification.content", "{elements: [{channels: [string], if: if, loop: loop, ref: ref, type: text}], version: '2022-01-01', scope: default}", + "--notification.name", "Welcome email", + "--notification.subscription", "{topic_id: topic_id}", + "--notification.tags", "[string]", + "--provider-key", "x", + "--state", "state", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "channel: email\n" + + "notification:\n" + + " brand:\n" + + " id: id\n" + + " content:\n" + + " elements:\n" + + " - channels:\n" + + " - string\n" + + " if: if\n" + + " loop: loop\n" + + " ref: ref\n" + + " type: text\n" + + " version: '2022-01-01'\n" + + " scope: default\n" + + " name: Welcome email\n" + + " subscription:\n" + + " topic_id: topic_id\n" + + " tags:\n" + + " - string\n" + + "providerKey: x\n" + + "state: state\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "journeys:templates", "create", + "--template-id", "x", + ) + }) +} + +func TestJourneysTemplatesRetrieve(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "retrieve", + "--template-id", "x", + "--notification-id", "x", + ) + }) +} + +func TestJourneysTemplatesList(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "list", + "--template-id", "x", + "--cursor", "cursor", + "--limit", "1", + ) + }) +} + +func TestJourneysTemplatesArchive(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "archive", + "--template-id", "x", + "--notification-id", "x", + ) + }) +} + +func TestJourneysTemplatesListVersions(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "list-versions", + "--template-id", "x", + "--notification-id", "x", + ) + }) +} + +func TestJourneysTemplatesPublish(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "publish", + "--template-id", "x", + "--notification-id", "x", + "--version", "v321669910225", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("version: v321669910225") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "journeys:templates", "publish", + "--template-id", "x", + "--notification-id", "x", + ) + }) +} + +func TestJourneysTemplatesReplace(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "replace", + "--template-id", "x", + "--notification-id", "x", + "--notification", "{brand: {id: id}, content: {elements: [{channels: [string], if: if, loop: loop, ref: ref, type: text}], version: '2022-01-01', scope: default}, name: name, subscription: {topic_id: topic_id}, tags: [string]}", + "--state", "state", + ) + }) + + t.Run("inner flags", func(t *testing.T) { + // Check that inner flags have been set up correctly + requestflag.CheckInnerFlags(journeysTemplatesReplace) + + // Alternative argument passing style using inner flags + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "journeys:templates", "replace", + "--template-id", "x", + "--notification-id", "x", + "--notification.brand", "{id: id}", + "--notification.content", "{elements: [{channels: [string], if: if, loop: loop, ref: ref, type: text}], version: '2022-01-01', scope: default}", + "--notification.name", "name", + "--notification.subscription", "{topic_id: topic_id}", + "--notification.tags", "[string]", + "--state", "state", + ) + }) + + t.Run("piping data", func(t *testing.T) { + // Test piping YAML data over stdin + pipeData := []byte("" + + "notification:\n" + + " brand:\n" + + " id: id\n" + + " content:\n" + + " elements:\n" + + " - channels:\n" + + " - string\n" + + " if: if\n" + + " loop: loop\n" + + " ref: ref\n" + + " type: text\n" + + " version: '2022-01-01'\n" + + " scope: default\n" + + " name: name\n" + + " subscription:\n" + + " topic_id: topic_id\n" + + " tags:\n" + + " - string\n" + + "state: state\n") + mocktest.TestRunMockTestWithPipeAndFlags( + t, pipeData, + "--api-key", "string", + "journeys:templates", "replace", + "--template-id", "x", + "--notification-id", "x", + ) + }) +} From 5ad24c484316bfbfe4e89a50ea3f98d55c897ea2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 17:19:00 +0000 Subject: [PATCH 22/29] feat: [SUP-607] Add DELETE endpoint for Courier Create tenant templates --- .stats.yml | 8 ++--- pkg/cmd/cmd.go | 1 + pkg/cmd/tenanttemplate.go | 54 ++++++++++++++++++++++++++++++++++ pkg/cmd/tenanttemplate_test.go | 13 ++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index eb39c6b..d43ae96 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 116 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-389afcb57163d4391f72d240c33a82d0522b958400603f137981480cda54558a.yml -openapi_spec_hash: c861d7fcd81ad5bfdaf8da0f5a84f497 -config_hash: e40fec72c1ab30fded57c222ace689b5 +configured_endpoints: 117 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-ebe0d758a32897eaf20f252329168bbe2c2da7f580bb871357e63f4b451c684e.yml +openapi_spec_hash: ba0c3cf85aa82f17744e217258a55b2e +config_hash: 10bd597dd6cc89023541bc551b6532b8 diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 42d7a85..027b1ef 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -354,6 +354,7 @@ func init() { Commands: []*cli.Command{ &tenantsTemplatesRetrieve, &tenantsTemplatesList, + &tenantsTemplatesDelete, &tenantsTemplatesPublish, &tenantsTemplatesReplace, }, diff --git a/pkg/cmd/tenanttemplate.go b/pkg/cmd/tenanttemplate.go index de615db..0234351 100644 --- a/pkg/cmd/tenanttemplate.go +++ b/pkg/cmd/tenanttemplate.go @@ -59,6 +59,26 @@ var tenantsTemplatesList = cli.Command{ HideHelpCommand: true, } +var tenantsTemplatesDelete = cli.Command{ + Name: "delete", + Usage: "Deletes the tenant's notification template with the given `template_id`.", + Suggest: true, + Flags: []cli.Flag{ + &requestflag.Flag[string]{ + Name: "tenant-id", + Required: true, + PathParam: "tenant_id", + }, + &requestflag.Flag[string]{ + Name: "template-id", + Required: true, + PathParam: "template_id", + }, + }, + Action: handleTenantsTemplatesDelete, + HideHelpCommand: true, +} + var tenantsTemplatesPublish = cli.Command{ Name: "publish", Usage: "Publishes a specific version of a notification template for a tenant.", @@ -236,6 +256,40 @@ func handleTenantsTemplatesList(ctx context.Context, cmd *cli.Command) error { }) } +func handleTenantsTemplatesDelete(ctx context.Context, cmd *cli.Command) error { + client := courier.NewClient(getDefaultRequestOptions(cmd)...) + unusedArgs := cmd.Args().Slice() + if !cmd.IsSet("template-id") && len(unusedArgs) > 0 { + cmd.Set("template-id", unusedArgs[0]) + unusedArgs = unusedArgs[1:] + } + if len(unusedArgs) > 0 { + return fmt.Errorf("Unexpected extra arguments: %v", unusedArgs) + } + + options, err := flagOptions( + cmd, + apiquery.NestedQueryFormatBrackets, + apiquery.ArrayQueryFormatComma, + EmptyBody, + false, + ) + if err != nil { + return err + } + + params := courier.TenantTemplateDeleteParams{ + TenantID: cmd.Value("tenant-id").(string), + } + + return client.Tenants.Templates.Delete( + ctx, + cmd.Value("template-id").(string), + params, + options..., + ) +} + func handleTenantsTemplatesPublish(ctx context.Context, cmd *cli.Command) error { client := courier.NewClient(getDefaultRequestOptions(cmd)...) unusedArgs := cmd.Args().Slice() diff --git a/pkg/cmd/tenanttemplate_test.go b/pkg/cmd/tenanttemplate_test.go index 22c58df..f580be6 100644 --- a/pkg/cmd/tenanttemplate_test.go +++ b/pkg/cmd/tenanttemplate_test.go @@ -36,6 +36,19 @@ func TestTenantsTemplatesList(t *testing.T) { }) } +func TestTenantsTemplatesDelete(t *testing.T) { + t.Skip("Mock server tests are disabled") + t.Run("regular flags", func(t *testing.T) { + mocktest.TestRunMockTestWithFlags( + t, + "--api-key", "string", + "tenants:templates", "delete", + "--tenant-id", "tenant_id", + "--template-id", "template_id", + ) + }) +} + func TestTenantsTemplatesPublish(t *testing.T) { t.Skip("Mock server tests are disabled") t.Run("regular flags", func(t *testing.T) { From be146558c9bdc2718a69a1622a588945c722d25d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 20:43:34 +0000 Subject: [PATCH 23/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d43ae96..ddb532d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-ebe0d758a32897eaf20f252329168bbe2c2da7f580bb871357e63f4b451c684e.yml -openapi_spec_hash: ba0c3cf85aa82f17744e217258a55b2e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-9c7c42e30eba90a90a7559c8442efb90604f8632e0d2354dba56b28921fe8bc2.yml +openapi_spec_hash: d144aadd6edbc5b99304da401b9b9e9a config_hash: 10bd597dd6cc89023541bc551b6532b8 From d2faa1c047accb92fb87b3f9d442f4456c01dc27 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 21:07:32 +0000 Subject: [PATCH 24/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ddb532d..a542136 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-9c7c42e30eba90a90a7559c8442efb90604f8632e0d2354dba56b28921fe8bc2.yml -openapi_spec_hash: d144aadd6edbc5b99304da401b9b9e9a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-d8dfdbe328b158491d3c3f4c9bb2dd4d92dc57f8582cdd4ef25c5b69ace1b859.yml +openapi_spec_hash: f657cb57513b19acc97edb5f95289a73 config_hash: 10bd597dd6cc89023541bc551b6532b8 From b8933e4fc72ad01bf64057d2360aafccae5019f2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 21:37:11 +0000 Subject: [PATCH 25/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a542136..efb115f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-d8dfdbe328b158491d3c3f4c9bb2dd4d92dc57f8582cdd4ef25c5b69ace1b859.yml -openapi_spec_hash: f657cb57513b19acc97edb5f95289a73 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-602215ef914077f1b7c18b9159f77ad23b8efe46da1022734cfb1a7d0d4d3d0d.yml +openapi_spec_hash: 2e56ad5f78a5cc71d639227c14683d20 config_hash: 10bd597dd6cc89023541bc551b6532b8 From d3898b183a57dc3665f7b4ef3784697cd5a5460f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 21:46:19 +0000 Subject: [PATCH 26/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index efb115f..58f9ecd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-602215ef914077f1b7c18b9159f77ad23b8efe46da1022734cfb1a7d0d4d3d0d.yml -openapi_spec_hash: 2e56ad5f78a5cc71d639227c14683d20 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-4c42b45dbeb17b835571e6d0e319517cdd77133c1c5a99ef9c942d974017157a.yml +openapi_spec_hash: 6705bdaf23c4ecf94646d565de5f158c config_hash: 10bd597dd6cc89023541bc551b6532b8 From 3e5f0cd321b17598cf89d2e9fcc0a364bbc379d2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 01:20:33 +0000 Subject: [PATCH 27/29] feat: [C-18380] Journeys API reference: copy + naming cleanup --- .stats.yml | 4 ++-- pkg/cmd/journey.go | 13 +++++++------ pkg/cmd/journeytemplate.go | 14 ++++++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.stats.yml b/.stats.yml index 58f9ecd..7f54643 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-4c42b45dbeb17b835571e6d0e319517cdd77133c1c5a99ef9c942d974017157a.yml -openapi_spec_hash: 6705bdaf23c4ecf94646d565de5f158c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-0addc822732cc48a729cc4c482fb1b7e47fd959adf40697f8db46eeb521f3c13.yml +openapi_spec_hash: 922cd04426937679b26ef6a4e2f54cc8 config_hash: 10bd597dd6cc89023541bc551b6532b8 diff --git a/pkg/cmd/journey.go b/pkg/cmd/journey.go index cc4c933..242e2ba 100644 --- a/pkg/cmd/journey.go +++ b/pkg/cmd/journey.go @@ -16,7 +16,7 @@ import ( var journeysCreate = cli.Command{ Name: "create", - Usage: "Create a new journey. The journey is created in DRAFT state. Use POST\n/journeys/{templateId}/publish to make it live.", + Usage: "Create a journey. Defaults to `DRAFT` state; pass `state: \"PUBLISHED\"` to\npublish on create. Send nodes are not allowed on `POST`. The standard flow is:\ncreate the journey shell here, add notification templates with\n`POST /journeys/{templateId}/templates`, then wire them into the journey with\n`PUT /journeys/{templateId}`. Call `POST /journeys/{templateId}/publish` to\npublish a draft after the fact.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -35,7 +35,7 @@ var journeysCreate = cli.Command{ }, &requestflag.Flag[string]{ Name: "state", - Usage: `Allowed values: "DRAFT", "PUBLISHED".`, + Usage: "Lifecycle state of a journey.", BodyPath: "state", }, }, @@ -55,6 +55,7 @@ var journeysRetrieve = cli.Command{ }, &requestflag.Flag[string]{ Name: "version", + Usage: "Version selector: `draft`, `published` (default), or `vN`.", QueryPath: "version", }, }, @@ -100,7 +101,7 @@ var journeysArchive = cli.Command{ var journeysInvoke = cli.Command{ Name: "invoke", - Usage: "Invoke a journey run from a journey template.", + Usage: "Invoke a journey by id or alias to start a new run. The response includes a\n`runId` identifying the run.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -145,7 +146,7 @@ var journeysListVersions = cli.Command{ var journeysPublish = cli.Command{ Name: "publish", - Usage: "Publish the current draft as a new version. Optionally rollback to a prior\nversion by passing `{ version: 'vN' }`.", + Usage: "Publish the current draft as a new version. Body is optional; pass\n`{ \"version\": \"vN\" }` to roll back to a prior version instead. Returns 404 if\nthe journey has no draft to publish.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -164,7 +165,7 @@ var journeysPublish = cli.Command{ var journeysReplace = cli.Command{ Name: "replace", - Usage: "Replace the journey draft. Updates the working draft only; call POST\n/journeys/{templateId}/publish to make it live.", + Usage: "Replace the journey draft. Updates the working draft only; call\n`POST /journeys/{templateId}/publish` to make it live, or pass\n`state: \"PUBLISHED\"` in this request to publish immediately. Send-node\n`template` ids must already exist and be scoped to this journey, and node ids\nmust not be claimed by another journey.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -188,7 +189,7 @@ var journeysReplace = cli.Command{ }, &requestflag.Flag[string]{ Name: "state", - Usage: `Allowed values: "DRAFT", "PUBLISHED".`, + Usage: "Lifecycle state of a journey.", BodyPath: "state", }, }, diff --git a/pkg/cmd/journeytemplate.go b/pkg/cmd/journeytemplate.go index 1ec8e96..2bb484b 100644 --- a/pkg/cmd/journeytemplate.go +++ b/pkg/cmd/journeytemplate.go @@ -16,7 +16,7 @@ import ( var journeysTemplatesCreate = requestflag.WithInnerFlags(cli.Command{ Name: "create", - Usage: "Create a notification template scoped to this journey. The template is created\nin DRAFT state.", + Usage: "Create a notification template scoped to this journey. Defaults to `DRAFT`\nstate; pass `state: \"PUBLISHED\"` to publish on create.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -92,7 +92,7 @@ var journeysTemplatesRetrieve = cli.Command{ var journeysTemplatesList = cli.Command{ Name: "list", - Usage: "List notification templates scoped to this journey. Templates scoped to a\njourney can only be referenced from `send` nodes of the same journey.", + Usage: "List notification templates scoped to this journey. Journey-scoped notification\ntemplates can only be referenced from `send` nodes within the same journey.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -102,10 +102,12 @@ var journeysTemplatesList = cli.Command{ }, &requestflag.Flag[string]{ Name: "cursor", + Usage: "Pagination cursor from a prior response.", QueryPath: "cursor", }, &requestflag.Flag[int64]{ Name: "limit", + Usage: "Page size. Minimum 1, maximum 100.", QueryPath: "limit", }, }, @@ -115,7 +117,7 @@ var journeysTemplatesList = cli.Command{ var journeysTemplatesArchive = cli.Command{ Name: "archive", - Usage: "Archive a journey-scoped notification template. Archived templates cannot be\nsent.", + Usage: "Archive the journey-scoped notification template. Archived templates cannot be\nsent.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -135,7 +137,7 @@ var journeysTemplatesArchive = cli.Command{ var journeysTemplatesListVersions = cli.Command{ Name: "list-versions", - Usage: "List published versions of a journey-scoped notification template, ordered most\nrecent first.", + Usage: "List published versions of the journey-scoped notification template, ordered\nmost recent first.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -155,7 +157,7 @@ var journeysTemplatesListVersions = cli.Command{ var journeysTemplatesPublish = cli.Command{ Name: "publish", - Usage: "Publish the current draft of a journey-scoped notification template.", + Usage: "Publish the current draft of the journey-scoped notification template as a new\nversion. Optionally roll back to a prior version by passing\n`{ \"version\": \"vN\" }`.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ @@ -179,7 +181,7 @@ var journeysTemplatesPublish = cli.Command{ var journeysTemplatesReplace = requestflag.WithInnerFlags(cli.Command{ Name: "replace", - Usage: "Replace a journey-scoped notification template draft.", + Usage: "Replace the journey-scoped notification template draft.", Suggest: true, Flags: []cli.Flag{ &requestflag.Flag[string]{ From f658cbecdff623b8d74b1803d26fe647eb9571ae Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 16:21:05 +0000 Subject: [PATCH 28/29] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7f54643..17321d4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 117 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-0addc822732cc48a729cc4c482fb1b7e47fd959adf40697f8db46eeb521f3c13.yml -openapi_spec_hash: 922cd04426937679b26ef6a4e2f54cc8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-8e7ad3d889c555ff9c381518b627b24b85e3eb7376bdc3689adc7a96ec78e537.yml +openapi_spec_hash: 53b3680aae719487c56efaa782bbe5b2 config_hash: 10bd597dd6cc89023541bc551b6532b8 From 8709fbc027419c2b83a610b387e5a8125f430fdc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 16:21:51 +0000 Subject: [PATCH 29/29] release: 3.5.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++ pkg/cmd/version.go | 2 +- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index acdf570..bf0d036 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.4.2" + ".": "3.5.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e0004b..b8d3766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## 3.5.0 (2026-05-19) + +Full Changelog: [v3.4.2...v3.5.0](https://github.com/trycourier/courier-cli/compare/v3.4.2...v3.5.0) + +### Features + +* [C-18380] Journeys API reference: copy + naming cleanup ([3e5f0cd](https://github.com/trycourier/courier-cli/commit/3e5f0cd321b17598cf89d2e9fcc0a364bbc379d2)) +* [SUP-607] Add DELETE endpoint for Courier Create tenant templates ([5ad24c4](https://github.com/trycourier/courier-cli/commit/5ad24c484316bfbfe4e89a50ea3f98d55c897ea2)) +* **api:** add journeys methods and journeys:templates resource ([f4afb09](https://github.com/trycourier/courier-cli/commit/f4afb0962c0d224f65d5c3d2061629787ada86b1)) +* **cli:** add `--raw-output`/`-r` option to print raw (non-JSON) strings ([780a9c0](https://github.com/trycourier/courier-cli/commit/780a9c0e5dc5f889e1756e00b7712b58fd1c2cf6)) +* **cli:** alias parameters in data with `x-stainless-cli-data-alias` ([4c52102](https://github.com/trycourier/courier-cli/commit/4c5210271156ddab475383aa6d512d915c43e7b6)) +* **cli:** send filename and content type when reading input from files ([8de2857](https://github.com/trycourier/courier-cli/commit/8de28579f6a831bfdc78ec33b16bcf31c55d06bb)) +* support passing path and query params over stdin ([29f9b3f](https://github.com/trycourier/courier-cli/commit/29f9b3f993fbd72360b1ce65e77b26e03bbfce8f)) + + +### Bug Fixes + +* **cli:** correctly load zsh autocompletion ([c58ffcd](https://github.com/trycourier/courier-cli/commit/c58ffcd91a7c0fb5255b638a34a61c2c8146759c)) +* flags for nullable body scalar fields are strictly typed ([41b8096](https://github.com/trycourier/courier-cli/commit/41b809656f1b5a1d4ba73b91e9c60533f032e5f0)) + + +### Chores + +* **ci:** support manually triggering release workflow ([99e6955](https://github.com/trycourier/courier-cli/commit/99e6955357951c874d777c4a26a920d602b7ec72)) +* **cli:** fall back to JSON when using default "explore" with non-TTY ([09aeebd](https://github.com/trycourier/courier-cli/commit/09aeebd03bb1402bd278940cdeb8ea69b01517b6)) +* **cli:** switch long lists of positional args over to param structs ([389c820](https://github.com/trycourier/courier-cli/commit/389c8207c4c54f657dfd310ce5ab1d8d751cc357)) +* **cli:** use `ShowJSONOpts` as argument to `formatJSON` instead of many positionals ([747c709](https://github.com/trycourier/courier-cli/commit/747c7090e140eb34ff022c5d9e42454618efe5c4)) +* **internal:** codegen related update ([b152ea0](https://github.com/trycourier/courier-cli/commit/b152ea0392fc2ee9857dde8affe7746cc07fa0cd)) +* **internal:** codegen related update ([cd851ac](https://github.com/trycourier/courier-cli/commit/cd851acc33bb7aa94374078ce84ca0e69064eec3)) +* **internal:** codegen related update ([0f66682](https://github.com/trycourier/courier-cli/commit/0f66682369f79620f487884c535e41ad96304d95)) +* **internal:** codegen related update ([8f3872f](https://github.com/trycourier/courier-cli/commit/8f3872f856ca43e04183c8f965f8e0e7ec1e0177)) +* **internal:** more robust bootstrap script ([3934247](https://github.com/trycourier/courier-cli/commit/39342478242318700e674e0ed42b1950e7bce5a1)) +* redact api-key headers in debug logs ([26b9617](https://github.com/trycourier/courier-cli/commit/26b96175c773dae96c8eb7a877126c309f71040f)) + + +### Documentation + +* **api:** update notification parameter descriptions ([40e76a4](https://github.com/trycourier/courier-cli/commit/40e76a403eab7c2c445e532fcbce8494b9cef78a)) + ## 3.4.2 (2026-04-14) Full Changelog: [v3.4.1...v3.4.2](https://github.com/trycourier/courier-cli/compare/v3.4.1...v3.4.2) diff --git a/pkg/cmd/version.go b/pkg/cmd/version.go index a648943..1b99840 100644 --- a/pkg/cmd/version.go +++ b/pkg/cmd/version.go @@ -2,4 +2,4 @@ package cmd -const Version = "3.4.2" // x-release-please-version +const Version = "3.5.0" // x-release-please-version