diff --git a/internal/cli/args.go b/internal/cli/args.go index 623b6c4..43cd073 100644 --- a/internal/cli/args.go +++ b/internal/cli/args.go @@ -24,6 +24,23 @@ func requireArgs(argNames ...string) cobra.PositionalArgs { } } +// requireExactArg returns a positional argument validator that requires exactly +// one argument named name, producing friendly messages that match requireArgs style: +// +// - zero args: "missing . Usage: ..." +// - >1 args: "expects exactly one . Usage: ..." +func requireExactArg(name string) cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + switch { + case len(args) == 0: + return fmt.Errorf("missing %s. Usage: %s", name, cmd.UseLine()) + case len(args) > 1: + return fmt.Errorf("expects exactly one %s. Usage: %s", name, cmd.UseLine()) + } + return nil + } +} + // requireExactlyOneFlag validates that exactly one of the named flags is set. func requireExactlyOneFlag(cmd *cobra.Command, flagNames ...string) error { set := 0 diff --git a/internal/cli/gen_positional_test.go b/internal/cli/gen_positional_test.go new file mode 100644 index 0000000..d177629 --- /dev/null +++ b/internal/cli/gen_positional_test.go @@ -0,0 +1,211 @@ +package cli + +import ( + "fmt" + "strings" + "testing" +) + +// TestGenPositionalUseLine asserts the generated --help Use line carries the +// positional placeholder: an *_ids array op renders the singular id followed by +// the variadic marker; an *_id scalar op renders the single id; the override and +// int cases render their pinned field. +func TestGenPositionalUseLine(t *testing.T) { + // (a) and (b) read the generated constructors directly (the curated commands + // own the live `incident ack`/`incident info` path-names, so the generated + // twins are dropped at registration but still constructible for assertion). + ack := genIncidentsAckCmd() + if got := ack.Use; got != "ack [...]" { + t.Errorf("ack twin Use = %q, want %q", got, "ack [...]") + } + if ack.Args == nil { + t.Errorf("ack twin has no Args validator") + } + if err := ack.Args(ack, nil); err == nil { + t.Errorf("ack twin Args accepted zero args (want >=1)") + } + if err := ack.Args(ack, []string{"id1"}); err != nil { + t.Errorf("ack twin Args rejected one arg: %v", err) + } + + info := genIncidentsInfoCmd() + if got := info.Use; got != "info " { + t.Errorf("info twin Use = %q, want %q", got, "info ") + } + if info.Args == nil { + t.Errorf("info twin has no Args validator") + } + if err := info.Args(info, nil); err == nil { + t.Errorf("info twin Args accepted zero args (want exactly one)") + } + if err := info.Args(info, []string{"id1"}); err != nil { + t.Errorf("info twin Args rejected one arg: %v", err) + } + + // Override cases: merge pins target_incident_id (NOT source_incident_ids); + // war-room detail pins chat_id. + merge := genIncidentsMergeCmd() + if got, want := merge.Use, "merge "; got != want { + t.Errorf("merge override Use = %q, want %q", got, want) + } + if strings.Contains(merge.Use, "source-incident") { + t.Errorf("merge override Use leaked source_incident_ids: %q", merge.Use) + } + detail := genIncidentsWarRoomDetailCmd() + if got, want := detail.Use, "war-room-detail "; got != want { + t.Errorf("war-room detail override Use = %q, want %q", got, want) + } +} + +// TestGenPositionalScalarStringRuntime invokes a GENERATED-ONLY string-scalar +// command (`field info `) and asserts the positional folds into the +// request body under the wire key. +func TestGenPositionalScalarStringRuntime(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + + if _, err := execCommand("field", "info", "fld-123"); err != nil { + t.Fatalf("execCommand field info: %v", err) + } + if stub.lastPath != "/field/info" { + t.Fatalf("path = %q, want /field/info", stub.lastPath) + } + if got := stub.lastBody["field_id"]; got != "fld-123" { + t.Errorf("field_id = %#v, want fld-123", got) + } +} + +// TestGenPositionalSliceRuntime invokes a GENERATED-ONLY string-slice command +// (`alert list-by-ids [...]`, whose alert_ids field is []string) +// and asserts every positional arg folds into the wire array verbatim. +func TestGenPositionalSliceRuntime(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + + if _, err := execCommand("alert", "list-by-ids", "a1", "a2", "a3"); err != nil { + t.Fatalf("execCommand alert list-by-ids: %v", err) + } + if stub.lastPath != "/alert/list-by-ids" { + t.Fatalf("path = %q, want /alert/list-by-ids", stub.lastPath) + } + if got, want := fmt.Sprint(stub.bodyStrings("alert_ids")), "[a1 a2 a3]"; got != want { + t.Errorf("alert_ids = %q, want %q", got, want) + } +} + +// TestGenPositionalIntSliceRuntime invokes a GENERATED-ONLY int-slice command +// (`team infos [...]`, whose team_ids field is []uint64) and +// asserts each positional arg is PARSED to an int in the wire array. A raw +// []string fold (the wrong kind) would fail SDK binding, so this guards the +// intslice path specifically. +func TestGenPositionalIntSliceRuntime(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + + if _, err := execCommand("team", "infos", "11", "22", "33"); err != nil { + t.Fatalf("execCommand team infos: %v", err) + } + if stub.lastPath != "/team/infos" { + t.Fatalf("path = %q, want /team/infos", stub.lastPath) + } + raw, ok := stub.lastBody["team_ids"].([]any) + if !ok || len(raw) != 3 { + t.Fatalf("team_ids = %#v, want a 3-element array", stub.lastBody["team_ids"]) + } + // JSON numbers decode to float64 through the stub. + for i, want := range []float64{11, 22, 33} { + if got, _ := raw[i].(float64); got != want { + t.Errorf("team_ids[%d] = %#v, want %v", i, raw[i], want) + } + } +} + +// TestGenPositionalIntRuntime invokes a GENERATED-ONLY int-scalar command +// (`schedule info `) and asserts the positional parses to an int +// in the wire body (schedule_id is Int64Var, so genFoldPositional uses ParseInt). +func TestGenPositionalIntRuntime(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + + // schedule info also needs --start/--end (relative-time required flags); supply + // them so the command reaches the wire. The positional is the assertion target. + if _, err := execCommand("schedule", "info", "4242", "--start", "now", "--end", "now"); err != nil { + t.Fatalf("execCommand schedule info: %v", err) + } + if stub.lastPath != "/schedule/info" { + t.Fatalf("path = %q, want /schedule/info", stub.lastPath) + } + // JSON numbers decode to float64 through the stub. + if got, _ := stub.lastBody["schedule_id"].(float64); got != 4242 { + t.Errorf("schedule_id = %#v, want 4242", stub.lastBody["schedule_id"]) + } +} + +// TestGenPositionalFlagOverridesPositional asserts the overlay order: an +// explicitly-set typed flag for the same field wins over the positional arg +// (positional folds first, the changed flag stamps after). +func TestGenPositionalFlagOverridesPositional(t *testing.T) { + saveAndResetGlobals(t) + stub := newGFStub(t) + + if _, err := execCommand("field", "info", "fromArg", "--field-id", "fromFlag"); err != nil { + t.Fatalf("execCommand field info with flag: %v", err) + } + if got := stub.lastBody["field_id"]; got != "fromFlag" { + t.Errorf("field_id = %#v, want fromFlag (explicit flag must override positional)", got) + } +} + +// TestGenFoldPositional unit-tests the runtime helper across all three kinds. +func TestGenFoldPositional(t *testing.T) { + // string scalar → args[0] + b := map[string]any{} + if err := genFoldPositional([]string{"abc"}, b, "x_id", "string"); err != nil { + t.Fatalf("string: %v", err) + } + if b["x_id"] != "abc" { + t.Errorf("string: x_id = %#v, want abc", b["x_id"]) + } + + // string slice → all args + b = map[string]any{} + if err := genFoldPositional([]string{"a", "b"}, b, "x_ids", "slice"); err != nil { + t.Fatalf("slice: %v", err) + } + if got, want := fmt.Sprint(b["x_ids"]), "[a b]"; got != want { + t.Errorf("slice: x_ids = %q, want %q", got, want) + } + + // int slice → each arg parsed to int64 + b = map[string]any{} + if err := genFoldPositional([]string{"1", "2"}, b, "x_ids", "intslice"); err != nil { + t.Fatalf("intslice: %v", err) + } + if got, want := fmt.Sprint(b["x_ids"]), "[1 2]"; got != want { + t.Errorf("intslice: x_ids = %q, want %q", got, want) + } + if _, ok := b["x_ids"].([]int64); !ok { + t.Errorf("intslice: x_ids type = %T, want []int64", b["x_ids"]) + } + + // int slice with non-numeric arg → clean error + b = map[string]any{} + if err := genFoldPositional([]string{"1", "x"}, b, "x_ids", "intslice"); err == nil { + t.Errorf("intslice: expected error on non-numeric arg, got nil") + } + + // int scalar → ParseInt + b = map[string]any{} + if err := genFoldPositional([]string{"77"}, b, "x_id", "int"); err != nil { + t.Fatalf("int: %v", err) + } + if b["x_id"] != int64(77) { + t.Errorf("int: x_id = %#v, want int64(77)", b["x_id"]) + } + + // int scalar with non-numeric arg → clean error + b = map[string]any{} + if err := genFoldPositional([]string{"nope"}, b, "x_id", "int"); err == nil { + t.Errorf("int: expected error on non-numeric arg, got nil") + } +} diff --git a/internal/cli/gen_support.go b/internal/cli/gen_support.go index 845dd7d..f923104 100644 --- a/internal/cli/gen_support.go +++ b/internal/cli/gen_support.go @@ -6,6 +6,7 @@ import ( "io" "os" "reflect" + "strconv" "strings" "github.com/spf13/cobra" @@ -47,11 +48,12 @@ func resolveDataSource(dataFlag string) (string, error) { // genAssembleBody builds a request body map from an optional --data JSON blob // overlaid with explicitly-set typed flags. Flags win over --data so an agent // can pass a JSON skeleton and override one field. setFlags is called after the -// --data merge to stamp the changed scalar flags. +// --data merge to stamp the changed scalar flags; it may return an error (e.g. +// int-parse failure from a positional argument). // // The --data value accepts two source forms (see resolveDataSource): inline // JSON, or - to read STDIN. -func genAssembleBody(dataFlag string, setFlags func(body map[string]any)) (map[string]any, error) { +func genAssembleBody(dataFlag string, setFlags func(body map[string]any) error) (map[string]any, error) { dataJSON, err := resolveDataSource(dataFlag) if err != nil { return nil, err @@ -62,7 +64,9 @@ func genAssembleBody(dataFlag string, setFlags func(body map[string]any)) (map[s return nil, fmt.Errorf("invalid --data JSON: %w", err) } } - setFlags(body) + if err := setFlags(body); err != nil { + return nil, err + } return body, nil } @@ -84,6 +88,56 @@ func curatedLong(intro, service, method string) string { return intro } +// genFoldPositional folds a generated command's positional argument(s) into the +// request body under wire, BEFORE the typed flags are stamped. The flag for the +// same field is kept; folding the positional first lets an explicitly-set flag +// still override it (matching genAssembleBody's --data-then-flags overlay order). +// +// kind selects how args map onto the body, matching the emitted flag type: +// +// "string" — string scalar → body[wire] = args[0] +// "int" — int64 scalar → body[wire] = ParseInt(args[0]) (schedule_id) +// "slice" — []string variadic → body[wire] = args (string ids) +// "intslice" — []int64 variadic → body[wire] = [ParseInt(a) for a in args] +// (channel_ids, team_ids, … whose SDK field is []uint64) +// +// args is the validated positional slice; the cobra Args validator (requireArgs) +// guarantees the arity below before RunE runs, but the bounds checks keep this +// safe if it is ever called directly. A no-positional command never calls this. +func genFoldPositional(args []string, body map[string]any, wire, kind string) error { + switch kind { + case "slice": + if len(args) > 0 { + body[wire] = args + } + case "intslice": + if len(args) > 0 { + ids := make([]int64, len(args)) + for i, a := range args { + n, err := strconv.ParseInt(a, 10, 64) + if err != nil { + return fmt.Errorf("invalid %s %q: must be an integer", wire, a) + } + ids[i] = n + } + body[wire] = ids + } + case "int": + if len(args) > 0 { + n, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return fmt.Errorf("invalid %s %q: must be an integer", wire, args[0]) + } + body[wire] = n + } + default: // "string" + if len(args) > 0 { + body[wire] = args[0] + } + } + return nil +} + // genBindBody marshals the assembled body map into the typed request struct so // the call benefits from the SDK's wire encoding (nullable pointers, etc.). // diff --git a/internal/cli/monit_agent.go b/internal/cli/monit_agent.go index ed746b3..898aa85 100644 --- a/internal/cli/monit_agent.go +++ b/internal/cli/monit_agent.go @@ -78,11 +78,12 @@ keys in --data. // Assemble the body the standard way: --data (inline JSON or - // stdin) overlaid with the typed --target-* flags, mirroring // genAssembleBody's "typed flags override --data keys". - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { body["target_locator"] = targetLocator if cmd.Flags().Changed("target-kind") { body["target_kind"] = targetKind } + return nil }) if err != nil { return err diff --git a/internal/cli/zz_generated_a2a_agents.go b/internal/cli/zz_generated_a2a_agents.go index 40c398f..c218a57 100644 --- a/internal/cli/zz_generated_a2a_agents.go +++ b/internal/cli/zz_generated_a2a_agents.go @@ -12,7 +12,7 @@ func genA2aAgentsReadGetCmd() *cobra.Command { var dataJSON string var fAgentID string cmd := &cobra.Command{ - Use: "a2a-agent-get", + Use: "a2a-agent-get ", Short: "Get A2A agent detail", Long: `Get A2A agent detail. @@ -46,13 +46,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - team_id (integer) (required) — Owning team; 0 means account scope. - updated_at (integer) (required) — Last-update time as a Unix timestamp in seconds. `, + Args: requireExactArg("agent_id"), Example: ` flashduty safari a2a-agent-get --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "agent_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("agent-id") { body["agent_id"] = fAgentID } + return nil }) if err != nil { return err @@ -70,7 +75,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -123,7 +128,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty safari a2a-agent-list --data '{"include_account":true,"limit":20,"offset":0}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("include-account") { body["include_account"] = fIncludeAccount } @@ -136,6 +141,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -156,7 +162,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().Int64Var(&fLimit, "limit", 0, "Maximum number of rows to return; defaults to 20.") cmd.Flags().Int64Var(&fOffset, "offset", 0, "Number of rows to skip for pagination.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Restrict results to resources owned by these teams; intersected with the caller's visible set.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -198,7 +204,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty safari a2a-agent-create --data '{"agent_name":"Network Diagnostics Agent","auth_config":{"token":"secret"},"auth_type":"bearer","card_url":"https://agents.example.com/network-diag/.well-known/agent.json","description":"Runs traceroute and BGP-path analysis for network incidents.","streaming":true,"team_id":0}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("agent-name") { body["agent_name"] = fAgentName } @@ -226,6 +232,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -251,7 +258,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "JSON schema of the per-user secret; required when auth_mode is per_user_secret.") cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Whether the agent supports streaming responses.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team for the new agent; 0 for account scope.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -259,7 +266,7 @@ func genA2aAgentsWriteDeleteCmd() *cobra.Command { var dataJSON string var fAgentID string cmd := &cobra.Command{ - Use: "a2a-agent-delete", + Use: "a2a-agent-delete ", Short: "Delete A2A agent", Long: `Delete A2A agent. @@ -270,13 +277,18 @@ API: POST /safari/a2a-agent/delete (remote-agent-write-delete) Request fields: --agent-id string (required) — Identifier of the target agent. `, + Args: requireExactArg("agent_id"), Example: ` flashduty safari a2a-agent-delete --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "agent_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("agent-id") { body["agent_id"] = fAgentID } + return nil }) if err != nil { return err @@ -294,7 +306,7 @@ Request fields: }, } cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -302,7 +314,7 @@ func genA2aAgentsWriteDisableCmd() *cobra.Command { var dataJSON string var fAgentID string cmd := &cobra.Command{ - Use: "a2a-agent-disable", + Use: "a2a-agent-disable ", Short: "Disable A2A agent", Long: `Disable A2A agent. @@ -313,13 +325,18 @@ API: POST /safari/a2a-agent/disable (remote-agent-write-disable) Request fields: --agent-id string (required) — Identifier of the target agent. `, + Args: requireExactArg("agent_id"), Example: ` flashduty safari a2a-agent-disable --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "agent_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("agent-id") { body["agent_id"] = fAgentID } + return nil }) if err != nil { return err @@ -337,7 +354,7 @@ Request fields: }, } cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -345,7 +362,7 @@ func genA2aAgentsWriteEnableCmd() *cobra.Command { var dataJSON string var fAgentID string cmd := &cobra.Command{ - Use: "a2a-agent-enable", + Use: "a2a-agent-enable ", Short: "Enable A2A agent", Long: `Enable A2A agent. @@ -356,13 +373,18 @@ API: POST /safari/a2a-agent/enable (remote-agent-write-enable) Request fields: --agent-id string (required) — Identifier of the target agent. `, + Args: requireExactArg("agent_id"), Example: ` flashduty safari a2a-agent-enable --data '{"agent_id":"a2a_9d4c1f60b3a2"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "agent_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("agent-id") { body["agent_id"] = fAgentID } + return nil }) if err != nil { return err @@ -380,7 +402,7 @@ Request fields: }, } cmd.Flags().StringVar(&fAgentID, "agent-id", "", "Identifier of the target agent. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -397,7 +419,7 @@ func genA2aAgentsWriteUpdateCmd() *cobra.Command { var fStreaming bool var fTeamID int64 cmd := &cobra.Command{ - Use: "a2a-agent-update", + Use: "a2a-agent-update ", Short: "Update A2A agent", Long: `Update A2A agent. @@ -418,10 +440,14 @@ Request fields: --team-id int — Reassign the agent to this team; omit to leave unchanged, 0 for account scope. auth_config (object, via --data) — New authentication parameters. `, + Args: requireExactArg("agent_id"), Example: ` flashduty safari a2a-agent-update --data '{"agent_id":"a2a_9d4c1f60b3a2","description":"Runs traceroute, BGP, and DNS analysis for network incidents."}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "agent_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("agent-id") { body["agent_id"] = fAgentID } @@ -452,6 +478,7 @@ Request fields: if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -478,7 +505,7 @@ Request fields: cmd.Flags().StringVar(&fSecretSchema, "secret-schema", "", "New per-user secret JSON schema.") cmd.Flags().BoolVar(&fStreaming, "streaming", false, "Toggle streaming-response support.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign the agent to this team; omit to leave unchanged, 0 for account scope.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_account.go b/internal/cli/zz_generated_account.go index b03fe2b..adfd89f 100644 --- a/internal/cli/zz_generated_account.go +++ b/internal/cli/zz_generated_account.go @@ -37,7 +37,8 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty account info --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -51,7 +52,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_alert_enrichment.go b/internal/cli/zz_generated_alert_enrichment.go index 5fad20e..4e2ca6e 100644 --- a/internal/cli/zz_generated_alert_enrichment.go +++ b/internal/cli/zz_generated_alert_enrichment.go @@ -12,7 +12,7 @@ func genAlertEnrichmentEnrichmentReadInfoCmd() *cobra.Command { var dataJSON string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get enrichment rules", Long: `Get enrichment rules. @@ -38,13 +38,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) (required) — Last update timestamp, Unix seconds. - updated_by (integer) (required) — Last updater member ID. `, + Args: requireExactArg("integration_id"), Example: ` flashduty enrichment info --data '{"integration_id":5001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -62,7 +67,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID to query enrichment rules for. Must be greater than 0. (required) (min 1)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -70,7 +75,7 @@ func genAlertEnrichmentEnrichmentReadListCmd() *cobra.Command { var dataJSON string var fIntegrationIDs []int cmd := &cobra.Command{ - Use: "list", + Use: "list [...]", Short: "List enrichment rules", Long: `List enrichment rules. @@ -97,13 +102,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) — Last update timestamp, Unix seconds. - updated_by (integer) (required) — Last updater member ID. `, + Args: requireArgs("integration_ids"), Example: ` flashduty enrichment list --data '{"integration_ids":[5001,5002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("integration-ids") { body["integration_ids"] = fIntegrationIDs } + return nil }) if err != nil { return err @@ -121,7 +131,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "List of integration IDs to query. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -129,7 +139,7 @@ func genAlertEnrichmentEnrichmentWriteUpsertCmd() *cobra.Command { var dataJSON string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "upsert", + Use: "upsert ", Short: "Upsert enrichment rules", Long: `Upsert enrichment rules. @@ -147,13 +157,18 @@ Request fields: - kind (string) (required) — Rule type. 'extraction' extracts a label via regex or GJson. 'composition' builds a label from a template. 'mapping' looks up values from a schema or API. 'drop' removes labels. [extraction, composition, mapping, drop] - settings (any) (required) — Rule-kind–specific settings. The shape depends on 'kind'. `, + Args: requireExactArg("integration_id"), Example: ` flashduty enrichment upsert --data '{"integration_id":5001,"rules":[{"kind":"extraction","settings":{"override":true,"pattern":"(?P\u003cresult\u003eprod|staging|dev)","result_label":"environment","source_field":"labels.env"}},{"kind":"composition","settings":{"override":false,"result_label":"full_env","template":"{{.labels.region}}-{{.labels.environment}}"}}]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -175,7 +190,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID to configure enrichment rules for. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -183,7 +198,7 @@ func genAlertEnrichmentFieldReadInfoCmd() *cobra.Command { var dataJSON string var fFieldID string cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get field detail", Long: `Get field detail. @@ -211,13 +226,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_by (integer) (required) — Last updater member ID. - value_type (string) (required) — Stored value type. 'checkbox' is always 'bool'; 'single_select'/'multi_select'/'text' are always 'string'. [string, bool, float] `, + Args: requireExactArg("field_id"), Example: ` flashduty field info --data '{"field_id":"66e9d3a4f7c2b04a1c8a91b3"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "field_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("field-id") { body["field_id"] = fFieldID } + return nil }) if err != nil { return err @@ -235,7 +255,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fFieldID, "field-id", "", "Field ID — 24-character hex ObjectID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -281,7 +301,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty field list --data '{"asc":false,"orderby":"updated_at","query":"severity"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("asc") { body["asc"] = fAsc } @@ -294,6 +314,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("query") { body["query"] = fQuery } + return nil }) if err != nil { return err @@ -314,7 +335,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().Int64Var(&fCreatorID, "creator-id", 0, "Filter by creator member ID. Omit or send 'null' to skip.") cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort key. Defaults to backend ordering when omitted. [created_at, updated_at]") cmd.Flags().StringVar(&fQuery, "query", "", "Regex filter against 'field_name' and 'display_name'. Invalid regex is auto-escaped to literal substring match.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -351,7 +372,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty field create --data '{"default_value":"Medium","description":"Business severity tier.","display_name":"Severity Class","field_name":"severity_class","field_type":"single_select","options":["Critical","High","Medium","Low"],"value_type":"string"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -370,6 +391,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("value-type") { body["value_type"] = fValueType } + return nil }) if err != nil { return err @@ -392,7 +414,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fFieldType, "field-type", "", "Field input type. Immutable after creation. (required) [checkbox, multi_select, single_select, text]") cmd.Flags().StringSliceVar(&fOptions, "options", nil, "Required and non-empty for 'single_select'/'multi_select' (unique strings, each 1–200 chars). Must be omitted or empty for 'checkbox'/'text'.") cmd.Flags().StringVar(&fValueType, "value-type", "", "Stored value type. 'checkbox' requires 'bool'; 'single_select'/'multi_select'/'text' require 'string'. Immutable after creation. (required) [string, bool, float]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -400,7 +422,7 @@ func genAlertEnrichmentFieldWriteDeleteCmd() *cobra.Command { var dataJSON string var fFieldID string cmd := &cobra.Command{ - Use: "delete", + Use: "delete ", Short: "Delete field", Long: `Delete field. @@ -411,13 +433,18 @@ API: POST /field/delete (field-write-delete) Request fields: --field-id string (required) — Field ID — 24-character hex ObjectID. `, + Args: requireExactArg("field_id"), Example: ` flashduty field delete --data '{"field_id":"66e9d3a4f7c2b04a1c8a91b3"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "field_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("field-id") { body["field_id"] = fFieldID } + return nil }) if err != nil { return err @@ -439,7 +466,7 @@ Request fields: }, } cmd.Flags().StringVar(&fFieldID, "field-id", "", "Field ID — 24-character hex ObjectID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -450,7 +477,7 @@ func genAlertEnrichmentFieldWriteUpdateCmd() *cobra.Command { var fFieldID string var fOptions []string cmd := &cobra.Command{ - Use: "update", + Use: "update ", Short: "Update field", Long: `Update field. @@ -465,10 +492,14 @@ Request fields: --options []string — Replacement options list. Must obey the same per-type rules as create. default_value (any, via --data) — Replacement default value. Type must match the field's existing 'field_type'. `, + Args: requireExactArg("field_id"), Example: ` flashduty field update --data '{"default_value":"Medium","description":"Business severity tier.","display_name":"Severity Class","field_id":"66e9d3a4f7c2b04a1c8a91b3","options":["Critical","High","Medium","Low"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "field_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -481,6 +512,7 @@ Request fields: if cmd.Flags().Changed("options") { body["options"] = fOptions } + return nil }) if err != nil { return err @@ -505,7 +537,7 @@ Request fields: cmd.Flags().StringVar(&fDisplayName, "display-name", "", "New display name. Must remain unique within the account. (≤39 chars)") cmd.Flags().StringVar(&fFieldID, "field-id", "", "Field ID — 24-character hex ObjectID. (required)") cmd.Flags().StringSliceVar(&fOptions, "options", nil, "Replacement options list. Must obey the same per-type rules as create.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -513,7 +545,7 @@ func genAlertEnrichmentMappingAPIReadInfoCmd() *cobra.Command { var dataJSON string var fAPIID string cmd := &cobra.Command{ - Use: "mapping-api-info", + Use: "mapping-api-info ", Short: "Get mapping API detail", Long: `Get mapping API detail. @@ -540,13 +572,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_by (integer) (required) — Last updater member ID. - url (string) (required) — Endpoint URL. `, + Args: requireExactArg("api_id"), Example: ` flashduty enrichment mapping-api-info --data '{"api_id":"665f1a2b3c4d5e6f7a8b9c02"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "api_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("api-id") { body["api_id"] = fAPIID } + return nil }) if err != nil { return err @@ -564,7 +601,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fAPIID, "api-id", "", "Mapping API ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -600,7 +637,8 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty enrichment mapping-api-list --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -614,7 +652,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -653,7 +691,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty enrichment mapping-api-create --data '{"api_name":"CMDB API","description":"Query CMDB for host metadata","headers":{"X-Token":"mytoken"},"insecure_skip_verify":false,"retry_count":1,"timeout":2,"url":"https://cmdb.example.com/api/lookup"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("api-name") { body["api_name"] = fAPIName } @@ -675,6 +713,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("url") { body["url"] = fURL } + return nil }) if err != nil { return err @@ -698,7 +737,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") cmd.Flags().Int64Var(&fTimeout, "timeout", 0, "Request timeout in seconds (1–3). Default 2.") cmd.Flags().StringVar(&fURL, "url", "", "HTTP/HTTPS endpoint URL (max 500 chars). (required) (≤500 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -706,7 +745,7 @@ func genAlertEnrichmentMappingAPIWriteDeleteCmd() *cobra.Command { var dataJSON string var fAPIID string cmd := &cobra.Command{ - Use: "mapping-api-delete", + Use: "mapping-api-delete ", Short: "Delete mapping API", Long: `Delete mapping API. @@ -717,13 +756,18 @@ API: POST /enrichment/mapping/api/delete (mapping-api-write-delete) Request fields: --api-id string (required) — Mapping API ID (MongoDB ObjectID hex). `, + Args: requireExactArg("api_id"), Example: ` flashduty enrichment mapping-api-delete --data '{"api_id":"665f1a2b3c4d5e6f7a8b9c02"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "api_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("api-id") { body["api_id"] = fAPIID } + return nil }) if err != nil { return err @@ -745,7 +789,7 @@ Request fields: }, } cmd.Flags().StringVar(&fAPIID, "api-id", "", "Mapping API ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -760,7 +804,7 @@ func genAlertEnrichmentMappingAPIWriteUpdateCmd() *cobra.Command { var fTimeout int64 var fURL string cmd := &cobra.Command{ - Use: "mapping-api-update", + Use: "mapping-api-update ", Short: "Update mapping API", Long: `Update mapping API. @@ -779,10 +823,14 @@ Request fields: --url string — New endpoint URL (max 500 chars). (≤500 chars) headers (object, via --data) — New headers map (replaces existing). `, + Args: requireExactArg("api_id"), Example: ` flashduty enrichment mapping-api-update --data '{"api_id":"665f1a2b3c4d5e6f7a8b9c02","retry_count":1,"timeout":3}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "api_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("api-id") { body["api_id"] = fAPIID } @@ -807,6 +855,7 @@ Request fields: if cmd.Flags().Changed("url") { body["url"] = fURL } + return nil }) if err != nil { return err @@ -835,7 +884,7 @@ Request fields: cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID.") cmd.Flags().Int64Var(&fTimeout, "timeout", 0, "New timeout in seconds.") cmd.Flags().StringVar(&fURL, "url", "", "New endpoint URL (max 500 chars). (≤500 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -843,7 +892,7 @@ func genAlertEnrichmentMappingDataReadDownloadCmd() *cobra.Command { var dataJSON string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-data-download", + Use: "mapping-data-download ", Short: "Download mapping data as CSV", Long: `Download mapping data as CSV. @@ -854,13 +903,18 @@ API: POST /enrichment/mapping/data/download (mapping-data-read-download) Request fields: --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-data-download --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -878,7 +932,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -891,7 +945,7 @@ func genAlertEnrichmentMappingDataReadListCmd() *cobra.Command { var fOrderby string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-data-list", + Use: "mapping-data-list ", Short: "List mapping data", Long: `List mapping data. @@ -918,10 +972,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - search_after_ctx (string) — Cursor token for the next page. - total (integer) (required) — Total matching rows. `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-data-list --data '{"asc":false,"limit":20,"orderby":"updated_at","p":1,"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("page") { body["p"] = fP } @@ -940,6 +998,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -962,7 +1021,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending when 'true'.") cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -971,7 +1030,7 @@ func genAlertEnrichmentMappingDataWriteDeleteCmd() *cobra.Command { var fKeys []string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-data-delete", + Use: "mapping-data-delete ", Short: "Delete mapping data rows", Long: `Delete mapping data rows. @@ -983,16 +1042,21 @@ Request fields: --keys []string (required) — Keys of rows to delete. --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-data-delete --data '{"keys":["server01","server02"],"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("keys") { body["keys"] = fKeys } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -1015,7 +1079,7 @@ Request fields: } cmd.Flags().StringSliceVar(&fKeys, "keys", nil, "Keys of rows to delete. (required)") cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1023,7 +1087,7 @@ func genAlertEnrichmentMappingDataWriteTruncateCmd() *cobra.Command { var dataJSON string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-data-truncate", + Use: "mapping-data-truncate ", Short: "Truncate mapping data", Long: `Truncate mapping data. @@ -1034,13 +1098,18 @@ API: POST /enrichment/mapping/data/truncate (mapping-data-write-truncate) Request fields: --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-data-truncate --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -1062,7 +1131,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1086,13 +1155,14 @@ Request fields: Example: ` flashduty enrichment mapping-data-upload --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("file") { body["file"] = fFile } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -1115,7 +1185,7 @@ Request fields: } cmd.Flags().StringVar(&fFile, "file", "", "CSV file to upload.") cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (query parameter).") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1123,7 +1193,7 @@ func genAlertEnrichmentMappingDataWriteUpsertCmd() *cobra.Command { var dataJSON string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-data-upsert", + Use: "mapping-data-upsert ", Short: "Upsert mapping data rows", Long: `Upsert mapping data rows. @@ -1138,13 +1208,18 @@ Request fields: Response fields ('data' envelope is unwrapped — these fields are at the top level): - keys (array) (required) — Composite keys of upserted rows. `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-data-upsert --data '{"docs":[{"host":"server01","owner":"alice","service":"api","team":"sre"},{"host":"server02","owner":"bob","service":"gateway","team":"platform"}],"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -1162,7 +1237,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1170,7 +1245,7 @@ func genAlertEnrichmentMappingSchemaReadInfoCmd() *cobra.Command { var dataJSON string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-schema-info", + Use: "mapping-schema-info ", Short: "Get mapping schema detail", Long: `Get mapping schema detail. @@ -1194,13 +1269,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) — Last update timestamp, Unix seconds. - updated_by (integer) (required) — Last updater member ID. `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-schema-info --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -1218,7 +1298,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1251,7 +1331,8 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty enrichment mapping-schema-list --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -1265,7 +1346,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1299,7 +1380,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty enrichment mapping-schema-create --data '{"description":"Enrich alerts with CMDB data","result_labels":["owner","team","service"],"schema_name":"CMDB Lookup","source_labels":["host"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -1315,6 +1396,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -1336,7 +1418,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fSchemaName, "schema-name", "", "Unique schema name (max 39 chars). (required) (≤39 chars)") cmd.Flags().StringSliceVar(&fSourceLabels, "source-labels", nil, "Lookup key label names (1–3). Must not overlap with 'result_labels'. (required)") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. '0' means no team.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1344,7 +1426,7 @@ func genAlertEnrichmentMappingSchemaWriteDeleteCmd() *cobra.Command { var dataJSON string var fSchemaID string cmd := &cobra.Command{ - Use: "mapping-schema-delete", + Use: "mapping-schema-delete ", Short: "Delete mapping schema", Long: `Delete mapping schema. @@ -1355,13 +1437,18 @@ API: POST /enrichment/mapping/schema/delete (mapping-schema-write-delete) Request fields: --schema-id string (required) — Mapping schema ID (MongoDB ObjectID hex). `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-schema-delete --data '{"schema_id":"665f1a2b3c4d5e6f7a8b9c01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("schema-id") { body["schema_id"] = fSchemaID } + return nil }) if err != nil { return err @@ -1383,7 +1470,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Mapping schema ID (MongoDB ObjectID hex). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1394,7 +1481,7 @@ func genAlertEnrichmentMappingSchemaWriteUpdateCmd() *cobra.Command { var fSchemaName string var fTeamID int64 cmd := &cobra.Command{ - Use: "mapping-schema-update", + Use: "mapping-schema-update ", Short: "Update mapping schema", Long: `Update mapping schema. @@ -1408,10 +1495,14 @@ Request fields: --schema-name string — New schema name (max 39 chars). (≤39 chars) --team-id int — New owning team ID. '0' removes the team association. `, + Args: requireExactArg("schema_id"), Example: ` flashduty enrichment mapping-schema-update --data '{"description":"Updated description","schema_id":"665f1a2b3c4d5e6f7a8b9c01","schema_name":"CMDB Lookup v2"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schema_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -1424,6 +1515,7 @@ Request fields: if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -1448,7 +1540,7 @@ Request fields: cmd.Flags().StringVar(&fSchemaID, "schema-id", "", "Schema ID (MongoDB ObjectID hex). (required)") cmd.Flags().StringVar(&fSchemaName, "schema-name", "", "New schema name (max 39 chars). (≤39 chars)") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID. '0' removes the team association.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_alert_rules.go b/internal/cli/zz_generated_alert_rules.go index c3806c4..49cbdce 100644 --- a/internal/cli/zz_generated_alert_rules.go +++ b/internal/cli/zz_generated_alert_rules.go @@ -36,10 +36,11 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit rule-audit-detail --data '{"id":9001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -57,7 +58,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fID, "id", 0, "Audit record ID — the 'id' of an audit row returned by 'POST /monit/rule/audits', NOT the rule ID. Passing a rule ID returns HTTP 400. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -89,10 +90,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-audits --data '{"id":50001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -110,7 +112,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().Int64Var(&fID, "id", 0, "Rule ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -128,7 +130,8 @@ API: POST /monit/rule/counter/channel (monit-rule-read-counter-channel) Example: ` flashduty monit rule-counter-channel --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -142,7 +145,7 @@ API: POST /monit/rule/counter/channel (monit-rule-read-counter-channel) }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -160,7 +163,8 @@ API: POST /monit/rule/counter/node (monit-rule-read-counter-node) Example: ` flashduty monit rule-counter-node --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -174,7 +178,7 @@ API: POST /monit/rule/counter/node (monit-rule-read-counter-node) }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -198,7 +202,8 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-counter-status --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -212,7 +217,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -236,7 +241,8 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-counter-total --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -250,7 +256,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -275,7 +281,8 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-dstypes --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -289,7 +296,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -370,10 +377,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-export --data '{"ids":[50001]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("ids") { body["ids"] = fIDs } + return nil }) if err != nil { return err @@ -391,7 +399,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -482,10 +490,11 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit rule-info --data '{"id":50001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -503,7 +512,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fID, "id", 0, "Rule ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -544,10 +553,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-list-basic --data '{"folder_id":100}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("folder-id") { body["folder_id"] = fFolderID } + return nil }) if err != nil { return err @@ -565,7 +575,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().Int64Var(&fFolderID, "folder-id", 0, "Folder ID. 0 to list all accessible rules.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -743,7 +753,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit rule-create --data '{"channel_ids":[20001],"cron_pattern":"* * * * *","ds_list":["prometheus*"],"ds_type":"prometheus","enabled":true,"folder_id":100,"name":"CPU High","rule_configs":{"check_threshold":{"alerting_check_times":1,"critical":"A","enabled":true,"push_recovery_event":true,"recovery":{"mode":"invert"},"recovery_check_times":1},"queries":[{"expr":"avg(cpu_usage_idle) \u003c 10","name":"A"}]}}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -810,6 +820,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("updater-name") { body["updater_name"] = fUpdaterName } + return nil }) if err != nil { return err @@ -848,7 +859,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fUpdatedAt, "updated-at", 0, "Request field updated_at") cmd.Flags().Int64Var(&fUpdaterID, "updater-id", 0, "Request field updater_id") cmd.Flags().StringVar(&fUpdaterName, "updater-name", "", "Request field updater_name") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -870,10 +881,11 @@ Request fields: Example: ` flashduty monit rule-delete --data '{"id":50001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -895,7 +907,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fID, "id", 0, "Rule ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -917,10 +929,11 @@ Request fields: Example: ` flashduty monit rule-delete-batch --data '{"ids":[50001,50002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("ids") { body["ids"] = fIDs } + return nil }) if err != nil { return err @@ -942,7 +955,7 @@ Request fields: }, } cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -998,7 +1011,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-update-fields --data '{"enabled":false,"fields":["enabled"],"ids":[50001,50002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-ids") { body["channel_ids"] = fChannelIDs } @@ -1038,6 +1051,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' if cmd.Flags().Changed("repeat-total") { body["repeat_total"] = fRepeatTotal } + return nil }) if err != nil { return err @@ -1067,7 +1081,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs to update. (required)") cmd.Flags().Int64Var(&fRepeatInterval, "repeat-interval", 0, "Request field repeat_interval") cmd.Flags().Int64Var(&fRepeatTotal, "repeat-total", 0, "Request field repeat_total") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1089,7 +1103,8 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-import --data '[{"cron_pattern":"* * * * *","ds_list":["prometheus*"],"ds_type":"prometheus","enabled":true,"folder_id":100,"name":"CPU High","rule_configs":{"queries":[{"expr":"avg(cpu_usage_idle) \u003c 10","name":"A"}]}}]'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -1106,7 +1121,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1134,13 +1149,14 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-move --data '{"dest_folder_id":200,"ids":[50001,50002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("dest-folder-id") { body["dest_folder_id"] = fDestFolderID } if cmd.Flags().Changed("ids") { body["ids"] = fIDs } + return nil }) if err != nil { return err @@ -1159,7 +1175,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' } cmd.Flags().Int64Var(&fDestFolderID, "dest-folder-id", 0, "Destination folder ID. (required)") cmd.Flags().IntSliceVar(&fIDs, "ids", nil, "Rule IDs to move. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1187,10 +1203,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit rule-status --data '{"folder_id":100}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("folder-id") { body["folder_id"] = fFolderID } + return nil }) if err != nil { return err @@ -1208,7 +1225,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().Int64Var(&fFolderID, "folder-id", 0, "Folder ID. 0 for all.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1386,7 +1403,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit rule-update --data '{"cron_pattern":"* * * * *","ds_list":["prometheus*"],"ds_type":"prometheus","enabled":true,"folder_id":100,"id":50001,"name":"CPU High v2","rule_configs":{"queries":[{"expr":"avg(cpu_usage_idle) \u003c 5","name":"A"}]}}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -1453,6 +1470,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("updater-name") { body["updater_name"] = fUpdaterName } + return nil }) if err != nil { return err @@ -1491,7 +1509,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fUpdatedAt, "updated-at", 0, "Request field updated_at") cmd.Flags().Int64Var(&fUpdaterID, "updater-id", 0, "Request field updater_id") cmd.Flags().StringVar(&fUpdaterName, "updater-name", "", "Request field updater_name") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_alerts.go b/internal/cli/zz_generated_alerts.go index b6a2632..42821d8 100644 --- a/internal/cli/zz_generated_alerts.go +++ b/internal/cli/zz_generated_alerts.go @@ -82,7 +82,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -116,6 +116,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if okStartTime { body["start_time"] = vStartTime } + return nil }) if err != nil { return err @@ -143,7 +144,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field (ES field name). [event_time]") cmd.Flags().StringVar(&fSeverities, "severities", "", "Comma-separated severity filter, e.g. 'Critical,Warning'.") cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start of search window, Unix epoch seconds. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -151,7 +152,7 @@ func genAlertsReadEventListCmd() *cobra.Command { var dataJSON string var fAlertID string cmd := &cobra.Command{ - Use: "event-list", + Use: "event-list ", Short: "List events for an alert", Long: `List events for an alert. @@ -187,13 +188,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - title_rule (string) — Title template used to derive 'title' from labels. - updated_at (integer) — Record update time, Unix epoch seconds. `, + Args: requireExactArg("alert_id"), Example: ` flashduty alert event-list --data '{"alert_id":"663a1b2c3d4e5f6789abcdef"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "alert_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("alert-id") { body["alert_id"] = fAlertID } + return nil }) if err != nil { return err @@ -211,7 +217,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().StringVar(&fAlertID, "alert-id", "", "Alert ID (ObjectID hex string). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -224,7 +230,7 @@ func genAlertsReadFeedCmd() *cobra.Command { var fAsc bool var fTypes []string cmd := &cobra.Command{ - Use: "feed", + Use: "feed ", Short: "List alert activity feed", Long: `List alert activity feed. @@ -251,10 +257,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - type (string) (required) — Alert activity feed entry type. Each value identifies one alert lifecycle event; the matching 'detail' payload shape is determined by this field. | Type | Meaning | |---|---| | 'a_new' | Alert triggered. | | 'a_comm' | Comment added on the alert. | | 'a_close' | Alert closed. | [a_new, a_comm, a_close] - updated_at (integer) (required) — Last update timestamp in Unix epoch milliseconds. `, + Args: requireExactArg("alert_id"), Example: ` flashduty alert feed --data '{"alert_id":"663a1b2c3d4e5f6789abcdef","asc":false,"limit":20}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "alert_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("page") { body["p"] = fP } @@ -273,6 +283,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("types") { body["types"] = fTypes } + return nil }) if err != nil { return err @@ -295,7 +306,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fAlertID, "alert-id", "", "Alert ID. (required)") cmd.Flags().BoolVar(&fAsc, "asc", false, "Sort ascending.") cmd.Flags().StringSliceVar(&fTypes, "types", nil, "Filter by feed types.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -303,7 +314,7 @@ func genAlertsReadInfoCmd() *cobra.Command { var dataJSON string var fAlertID string cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get alert detail", Long: `Get alert detail. @@ -376,13 +387,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - title_rule (string) — Title template used to derive 'title' from the event labels (e.g. '$service::$cluster'). - updated_at (integer) — Last update timestamp, Unix epoch seconds. `, + Args: requireExactArg("alert_id"), Example: ` flashduty alert info --data '{"alert_id":"663a1b2c3d4e5f6789abcdef"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "alert_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("alert-id") { body["alert_id"] = fAlertID } + return nil }) if err != nil { return err @@ -400,7 +416,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fAlertID, "alert-id", "", "Alert ID (ObjectID hex string). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -524,7 +540,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -570,6 +586,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if okStartTime { body["start_time"] = vStartTime } + return nil }) if err != nil { return err @@ -601,7 +618,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().BoolVar(&fIsActive, "is-active", false, "Filter by active ('true') or resolved ('false') status.") cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start of the search window, Unix epoch seconds. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -609,7 +626,7 @@ func genAlertsReadListByIDsCmd() *cobra.Command { var dataJSON string var fAlertIDs []string cmd := &cobra.Command{ - Use: "list-by-ids", + Use: "list-by-ids [...]", Short: "List alerts by IDs", Long: `List alerts by IDs. @@ -686,13 +703,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - search_after_ctx (string) — Cursor for the next page. - total (integer) — Total matching alerts. `, + Args: requireArgs("alert_ids"), Example: ` flashduty alert list-by-ids --data '{"alert_ids":["663a1b2c3d4e5f6789abcdef"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "alert_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("alert-ids") { body["alert_ids"] = fAlertIDs } + return nil }) if err != nil { return err @@ -710,7 +732,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().StringSliceVar(&fAlertIDs, "alert-ids", nil, "List of alert IDs (ObjectID hex strings). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -718,7 +740,7 @@ func genAlertsReadPipelineInfoCmd() *cobra.Command { var dataJSON string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "pipeline-info", + Use: "pipeline-info ", Short: "Get alert pipeline", Long: `Get alert pipeline. @@ -741,13 +763,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) — Last update timestamp, Unix epoch seconds. - updated_by (integer) — Member ID who last updated the pipeline. `, + Args: requireExactArg("integration_id"), Example: ` flashduty alert pipeline-info --data '{"integration_id":10001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -765,7 +792,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -773,7 +800,7 @@ func genAlertsReadPipelineListCmd() *cobra.Command { var dataJSON string var fIntegrationIDs []int cmd := &cobra.Command{ - Use: "pipeline-list", + Use: "pipeline-list [...]", Short: "List alert pipelines", Long: `List alert pipelines. @@ -797,13 +824,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) — Last update timestamp, Unix epoch seconds. - updated_by (integer) — Member ID who last updated the pipeline. `, + Args: requireArgs("integration_ids"), Example: ` flashduty alert pipeline-list --data '{"integration_ids":[10001,10002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("integration-ids") { body["integration_ids"] = fIntegrationIDs } + return nil }) if err != nil { return err @@ -821,7 +853,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Integration IDs. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -833,7 +865,7 @@ func genAlertsWriteMergeCmd() *cobra.Command { var fOwnerID int64 var fTitle string cmd := &cobra.Command{ - Use: "merge", + Use: "merge [...]", Short: "Merge alerts into an incident", Long: `Merge alerts into an incident. @@ -848,10 +880,14 @@ Request fields: --owner-id int — Optional new owner for the target incident. --title string — Optional new title for the target incident. `, + Args: requireArgs("alert_ids"), Example: ` flashduty alert merge --data '{"alert_ids":["663a1b2c3d4e5f6789abcdef"],"incident_id":"663a000000000000deadbeef"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "alert_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("alert-ids") { body["alert_ids"] = fAlertIDs } @@ -867,6 +903,7 @@ Request fields: if cmd.Flags().Changed("title") { body["title"] = fTitle } + return nil }) if err != nil { return err @@ -892,7 +929,7 @@ Request fields: cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Target incident ID. (required)") cmd.Flags().Int64Var(&fOwnerID, "owner-id", 0, "Optional new owner for the target incident.") cmd.Flags().StringVar(&fTitle, "title", "", "Optional new title for the target incident.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -900,7 +937,7 @@ func genAlertsWritePipelineUpsertCmd() *cobra.Command { var dataJSON string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "pipeline-upsert", + Use: "pipeline-upsert ", Short: "Create or update alert pipeline", Long: `Create or update alert pipeline. @@ -915,13 +952,18 @@ Request fields: - kind (string) — Rule type. [title_reset, description_reset, severity_reset, alert_drop, alert_inhibit] - settings (object) — Kind-specific settings. Shape depends on 'kind': - 'title_reset': '{ "title": "" }' - 'description_reset': '{ "description": "" }' - 'severity_reset': '{ "severity": "Critical"|"Warning"|"Info" }' - 'alert_drop': '{}' (empty object) - 'alert_inhibit': '{ "equals": ["", ...], "source_filters": }' `, + Args: requireExactArg("integration_id"), Example: ` flashduty alert pipeline-upsert --data '{"integration_id":10001,"rules":[{"if":null,"kind":"severity_reset","settings":{"severity":"Warning"}}]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -943,7 +985,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID to configure. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_analytics.go b/internal/cli/zz_generated_analytics.go index 5812d9b..22b0ebe 100644 --- a/internal/cli/zz_generated_analytics.go +++ b/internal/cli/zz_generated_analytics.go @@ -107,7 +107,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -168,6 +168,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -204,7 +205,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -307,7 +308,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -368,6 +369,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -404,7 +406,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -498,7 +500,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -559,6 +561,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -595,7 +598,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -698,7 +701,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -759,6 +762,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -795,7 +799,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -865,7 +869,7 @@ Request fields: if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -926,6 +930,7 @@ Request fields: if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -966,7 +971,7 @@ Request fields: cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1024,7 +1029,7 @@ Request fields: Example: ` flashduty insight incident-export --data '{"description_html_to_text":true,"end_time":1712604800,"export_fields":["incident_id","title","severity","created_at","seconds_to_close"],"severities":["Critical","Warning"],"start_time":1712000000}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("asc") { body["asc"] = fAsc } @@ -1079,6 +1084,7 @@ Request fields: if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -1117,7 +1123,7 @@ Request fields: cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Request field ") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Request field ") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "Request field ") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1231,7 +1237,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -1295,6 +1301,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -1332,7 +1339,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1402,7 +1409,7 @@ Request fields: if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -1463,6 +1470,7 @@ Request fields: if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -1503,7 +1511,7 @@ Request fields: cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1573,7 +1581,7 @@ Request fields: if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -1634,6 +1642,7 @@ Request fields: if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -1674,7 +1683,7 @@ Request fields: cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1755,7 +1764,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggregate-unit") { body["aggregate_unit"] = fAggregateUnit } @@ -1822,6 +1831,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -1860,7 +1870,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start time, Unix seconds. Must be greater than 0. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs. At most 100 entries.") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "IANA time zone name used to interpret the time range (e.g. 'Asia/Shanghai'). Defaults to the account time zone.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_applications.go b/internal/cli/zz_generated_applications.go index 0017887..4a75bae 100644 --- a/internal/cli/zz_generated_applications.go +++ b/internal/cli/zz_generated_applications.go @@ -12,7 +12,7 @@ func genApplicationsReadInfoCmd() *cobra.Command { var dataJSON string var fApplicationID string cmd := &cobra.Command{ - Use: "application-info", + Use: "application-info ", Short: "Get application detail", Long: `Get application detail. @@ -47,13 +47,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) — Last update timestamp, Unix epoch seconds. - updated_by (integer) — Last updater member ID. `, + Args: requireExactArg("application_id"), Example: ` flashduty rum application-info --data '{"application_id":"WoyQQ3BohkdtPivubEvE8o"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "application_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("application-id") { body["application_id"] = fApplicationID } + return nil }) if err != nil { return err @@ -71,7 +76,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fApplicationID, "application-id", "", "RUM application ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -79,7 +84,7 @@ func genApplicationsReadInfosCmd() *cobra.Command { var dataJSON string var fApplicationIDs []string cmd := &cobra.Command{ - Use: "application-infos", + Use: "application-infos [...]", Short: "Batch get applications", Long: `Batch get applications. @@ -115,13 +120,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) — Last update timestamp, Unix epoch seconds. - updated_by (integer) — Last updater member ID. `, + Args: requireArgs("application_ids"), Example: ` flashduty rum application-infos --data '{"application_ids":["eWbr4xk3ZRnLabRa6unqwD","WoyQQ3BohkdtPivubEvE8o"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "application_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("application-ids") { body["application_ids"] = fApplicationIDs } + return nil }) if err != nil { return err @@ -139,7 +149,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().StringSliceVar(&fApplicationIDs, "application-ids", nil, "Up to 200 application IDs. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -202,7 +212,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty rum application-list --data '{"is_my_team":false,"limit":20,"p":1,"query":""}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -227,6 +237,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -251,7 +262,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") cmd.Flags().StringVar(&fQuery, "query", "", "Search query to filter by application name.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Filter by team ID.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -264,7 +275,7 @@ func genApplicationsWriteCreateCmd() *cobra.Command { var fTeamID int64 var fType string cmd := &cobra.Command{ - Use: "application-create", + Use: "application-create ", Short: "Create application", Long: `Create application. @@ -293,10 +304,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - application_name (string) — Application display name. - client_token (string) — Token for RUM SDK initialization. `, + Args: requireExactArg("team_id"), Example: ` flashduty rum application-create --data '{"application_name":"My Web App","is_private":false,"team_id":2477033058131,"type":"browser"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "team_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("application-name") { body["application_name"] = fApplicationName } @@ -315,6 +330,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("type") { body["type"] = fType } + return nil }) if err != nil { return err @@ -337,7 +353,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fNoIP, "no-ip", false, "Do not collect IP addresses.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. (required)") cmd.Flags().StringVar(&fType, "type", "", "Application type. (required) [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -345,7 +361,7 @@ func genApplicationsWriteDeleteCmd() *cobra.Command { var dataJSON string var fApplicationID string cmd := &cobra.Command{ - Use: "application-delete", + Use: "application-delete ", Short: "Delete application", Long: `Delete application. @@ -356,13 +372,18 @@ API: POST /rum/application/delete (rum-application-write-delete) Request fields: --application-id string (required) — RUM application ID. `, + Args: requireExactArg("application_id"), Example: ` flashduty rum application-delete --data '{"application_id":"qLpu24Dz4CAzWsESPbJYWA"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "application_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("application-id") { body["application_id"] = fApplicationID } + return nil }) if err != nil { return err @@ -384,7 +405,7 @@ Request fields: }, } cmd.Flags().StringVar(&fApplicationID, "application-id", "", "RUM application ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -398,7 +419,7 @@ func genApplicationsWriteUpdateCmd() *cobra.Command { var fTeamID int64 var fType string cmd := &cobra.Command{ - Use: "application-update", + Use: "application-update ", Short: "Update application", Long: `Update application. @@ -423,10 +444,14 @@ Request fields: - endpoint (string) — Trace endpoint URL (http or https). - open_type (string) — How to open the trace link. [popup, tab] `, + Args: requireExactArg("application_id"), Example: ` flashduty rum application-update --data '{"alerting":{"channel_ids":[2490121812131],"enabled":true},"application_id":"WoyQQ3BohkdtPivubEvE8o","application_name":"My Web App v2"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "application_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("application-id") { body["application_id"] = fApplicationID } @@ -448,6 +473,7 @@ Request fields: if cmd.Flags().Changed("type") { body["type"] = fType } + return nil }) if err != nil { return err @@ -475,7 +501,7 @@ Request fields: cmd.Flags().BoolVar(&fNoIP, "no-ip", false, "Request field no_ip") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Request field team_id") cmd.Flags().StringVar(&fType, "type", "", "Request field type [browser, ios, android, react-native, flutter, kotlin-multiplatform, roku, unity]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_audit_logs.go b/internal/cli/zz_generated_audit_logs.go index 54d4632..7a5fe1e 100644 --- a/internal/cli/zz_generated_audit_logs.go +++ b/internal/cli/zz_generated_audit_logs.go @@ -27,7 +27,8 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty audit operation-list --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -41,7 +42,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -106,7 +107,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if okEndTime { body["end_time"] = vEndTime } @@ -134,6 +135,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if okStartTime { body["start_time"] = vStartTime } + return nil }) if err != nil { return err @@ -159,7 +161,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fRequestID, "request-id", "", "Filter to a single request by its unique request ID.") cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque pagination cursor returned by the previous response. Leave empty for the first page.") cmd.Flags().StringVar(&fStartTime, "start-time", "", "Start of the search window, Unix epoch seconds. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_calendars.go b/internal/cli/zz_generated_calendars.go index a45d864..a4d9219 100644 --- a/internal/cli/zz_generated_calendars.go +++ b/internal/cli/zz_generated_calendars.go @@ -28,13 +28,14 @@ Request fields: Example: ` flashduty calendar event-delete --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","event_id":"cale.KyG9XWTCU5CucbwukEVBQ4"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("cal-id") { body["cal_id"] = fCalID } if cmd.Flags().Changed("event-id") { body["event_id"] = fEventID } + return nil }) if err != nil { return err @@ -57,7 +58,7 @@ Request fields: } cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") cmd.Flags().StringVar(&fEventID, "event-id", "", "Event ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -68,7 +69,7 @@ func genCalendarsCalEventListCmd() *cobra.Command { var fMonth int64 var fYear int64 cmd := &cobra.Command{ - Use: "event-list", + Use: "event-list ", Short: "List calendar events", Long: `List calendar events. @@ -97,10 +98,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) — Last update timestamp (Unix seconds). - total (integer) (required) — Total number of events returned. `, + Args: requireExactArg("cal_id"), Example: ` flashduty calendar event-list --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","month":5,"year":2024}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "cal_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("cal-id") { body["cal_id"] = fCalID } @@ -113,6 +118,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("year") { body["year"] = fYear } + return nil }) if err != nil { return err @@ -133,7 +139,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().Int64Var(&fDay, "day", 0, "Day (1-31). 0 means no day filter. (0-31)") cmd.Flags().Int64Var(&fMonth, "month", 0, "Month (1-12). 0 means no month filter. (0-12)") cmd.Flags().Int64Var(&fYear, "year", 0, "Year. Defaults to the current year when omitted. (min 2023)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -147,7 +153,7 @@ func genCalendarsCalEventUpsertCmd() *cobra.Command { var fStartAt string var fSummary string cmd := &cobra.Command{ - Use: "event-upsert", + Use: "event-upsert ", Short: "Upsert calendar event", Long: `Upsert calendar event. @@ -169,10 +175,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - event_id (string) (required) — Event ID (existing or newly generated). - summary (string) (required) — Event summary. `, + Args: requireExactArg("cal_id"), Example: ` flashduty calendar event-upsert --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","description":"International Workers Day holiday","end_at":"2024-05-06","is_off":true,"start_at":"2024-05-01","summary":"Labour Day"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "cal_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("cal-id") { body["cal_id"] = fCalID } @@ -194,6 +204,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("summary") { body["summary"] = fSummary } + return nil }) if err != nil { return err @@ -217,7 +228,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fIsOff, "is-off", false, "Whether the event marks a non-working day. true = day off, false = working day override. (required)") cmd.Flags().StringVar(&fStartAt, "start-at", "", "Event start date in YYYY-MM-DD. (required)") cmd.Flags().StringVar(&fSummary, "summary", "", "Event summary. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -253,7 +264,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty calendar create --data '{"cal_name":"Production On-Call Calendar","description":"Calendar for production on-call team","timezone":"Asia/Shanghai","workdays":[1,2,3,4,5]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("cal-name") { body["cal_name"] = fCalName } @@ -272,6 +283,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("workdays") { body["workdays"] = fWorkdays } + return nil }) if err != nil { return err @@ -294,7 +306,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. 0 means no team.") cmd.Flags().StringVar(&fTimezone, "timezone", "", "IANA timezone. Defaults to Asia/Shanghai when empty.") cmd.Flags().IntSliceVar(&fWorkdays, "workdays", nil, "Workday numbers (0 = Sunday, 6 = Saturday).") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -302,7 +314,7 @@ func genCalendarsCalendarDeleteCmd() *cobra.Command { var dataJSON string var fCalID string cmd := &cobra.Command{ - Use: "delete", + Use: "delete ", Short: "Delete calendar", Long: `Delete calendar. @@ -313,13 +325,18 @@ API: POST /calendar/delete (calendarDelete) Request fields: --cal-id string (required) — Calendar ID. `, + Args: requireExactArg("cal_id"), Example: ` flashduty calendar delete --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "cal_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("cal-id") { body["cal_id"] = fCalID } + return nil }) if err != nil { return err @@ -341,7 +358,7 @@ Request fields: }, } cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -349,7 +366,7 @@ func genCalendarsCalendarInfoCmd() *cobra.Command { var dataJSON string var fCalID string cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get calendar info", Long: `Get calendar info. @@ -376,13 +393,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_by (integer) (required) — Last updater person ID. - workdays (array) — Workday numbers (0 = Sunday, 6 = Saturday). `, + Args: requireExactArg("cal_id"), Example: ` flashduty calendar info --data '{"cal_id":"cal.eh9gvPtWeH3xXgKeVSRxRg"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "cal_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("cal-id") { body["cal_id"] = fCalID } + return nil }) if err != nil { return err @@ -400,7 +422,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fCalID, "cal-id", "", "Calendar ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -442,13 +464,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty calendar list --data '{"kind":"personal"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("kind") { body["kind"] = fKind } if cmd.Flags().Changed("no-locale") { body["no_locale"] = fNoLocale } + return nil }) if err != nil { return err @@ -467,7 +490,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().StringVar(&fKind, "kind", "", "Calendar kind filter. Defaults to personal when empty. [region.official.holiday, personal]") cmd.Flags().BoolVar(&fNoLocale, "no-locale", false, "Disable locale filtering when listing public-holiday calendars.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -481,7 +504,7 @@ func genCalendarsCalendarUpdateCmd() *cobra.Command { var fTimezone string var fWorkdays []int cmd := &cobra.Command{ - Use: "update", + Use: "update ", Short: "Update calendar", Long: `Update calendar. @@ -498,10 +521,14 @@ Request fields: --timezone string — New IANA timezone. --workdays []int — Workday numbers (0 = Sunday, 6 = Saturday). `, + Args: requireExactArg("cal_id"), Example: ` flashduty calendar update --data '{"cal_id":"cal.QiNvtdKs4Wj52kZhT3LafM","cal_name":"Production On-Call Calendar (Updated)","timezone":"America/New_York","workdays":[1,2,3,4,5]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "cal_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("cal-id") { body["cal_id"] = fCalID } @@ -523,6 +550,7 @@ Request fields: if cmd.Flags().Changed("workdays") { body["workdays"] = fWorkdays } + return nil }) if err != nil { return err @@ -550,7 +578,7 @@ Request fields: cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID.") cmd.Flags().StringVar(&fTimezone, "timezone", "", "New IANA timezone.") cmd.Flags().IntSliceVar(&fWorkdays, "workdays", nil, "Workday numbers (0 = Sunday, 6 = Saturday).") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_changes.go b/internal/cli/zz_generated_changes.go index d1148bc..3203d1b 100644 --- a/internal/cli/zz_generated_changes.go +++ b/internal/cli/zz_generated_changes.go @@ -90,7 +90,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -124,6 +124,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if okStartTime { body["start_time"] = vStartTime } + return nil }) if err != nil { return err @@ -151,7 +152,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field to sort the result by. [start_time, last_time]") cmd.Flags().StringVar(&fQuery, "query", "", "Free-text or regular-expression search over change fields.") cmd.Flags().StringVar(&fStartTime, "start-time", "", "Unix timestamp in seconds for the start of the query window. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_channels.go b/internal/cli/zz_generated_channels.go index 7a60452..44c1e36 100644 --- a/internal/cli/zz_generated_channels.go +++ b/internal/cli/zz_generated_channels.go @@ -83,7 +83,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty channel create --data '{"auto_resolve_mode":"trigger","auto_resolve_timeout":86400,"channel_name":"Production Alerts","description":"Handles all production environment alerts","group":{"method":"p","time_window":10,"window_type":"tumbling"},"team_id":3521074710131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("auto-resolve-mode") { body["auto_resolve_mode"] = fAutoResolveMode } @@ -117,6 +117,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -144,7 +145,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().IntSliceVar(&fManagingTeamIDs, "managing-team-ids", nil, "Additional teams that can manage the channel. Up to 3 entries.") cmd.Flags().IntSliceVar(&fPluginIDs, "plugin-ids", nil, "IDs of plugins (integrations) subscribed to this channel.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -152,7 +153,7 @@ func genChannelsChannelDeleteCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "delete", + Use: "delete ", Short: "Delete channel", Long: `Delete channel. @@ -163,13 +164,18 @@ API: POST /channel/delete (channelDelete) Request fields: --channel-id int (required) — Channel ID. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel delete --data '{"channel_id":3521074710131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -191,7 +197,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -199,7 +205,7 @@ func genChannelsChannelDisableCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "disable", + Use: "disable ", Short: "Disable channel", Long: `Disable channel. @@ -210,13 +216,18 @@ API: POST /channel/disable (channelDisable) Request fields: --channel-id int (required) — Channel ID. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel disable --data '{"channel_id":3521074710131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -238,7 +249,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -246,7 +257,7 @@ func genChannelsChannelEnableCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "enable", + Use: "enable ", Short: "Enable channel", Long: `Enable channel. @@ -257,13 +268,18 @@ API: POST /channel/enable (channelEnable) Request fields: --channel-id int (required) — Channel ID. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel enable --data '{"channel_id":3521074710131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -285,7 +301,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -346,7 +362,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty channel escalate-rule-create --data '{"channel_id":3521074710131,"description":"Notify primary on-call, then escalate to secondary after 30 minutes","layers":[{"escalate_window":30,"force_escalate":false,"max_times":3,"notify_step":10,"target":{"by":{"follow_preference":true},"person_ids":[3790925372131]}}],"rule_name":"On-call escalation","template_id":"6321aad26c12104586a88916"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggr-window") { body["aggr_window"] = fAggrWindow } @@ -365,6 +381,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("template-id") { body["template_id"] = fTemplateID } + return nil }) if err != nil { return err @@ -387,7 +404,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first. (0-200)") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Notification template ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -411,13 +428,14 @@ Request fields: Example: ` flashduty channel escalate-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -440,7 +458,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -464,13 +482,14 @@ Request fields: Example: ` flashduty channel escalate-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -493,7 +512,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -517,13 +536,14 @@ Request fields: Example: ` flashduty channel escalate-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -546,7 +566,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -611,13 +631,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty channel escalate-rule-info --data '{"channel_id":1001,"rule_id":"6621b23f4a2c5e0012ab34d0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -636,7 +657,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -644,7 +665,7 @@ func genChannelsChannelEscalateRuleListCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "escalate-rule-list", + Use: "escalate-rule-list ", Short: "List escalation rules", Long: `List escalation rules. @@ -691,13 +712,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) — Last update timestamp (unix seconds). - updated_by (integer) (required) — Member ID that last updated the rule. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel escalate-rule-list --data '{"channel_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -715,7 +741,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -774,7 +800,7 @@ Request fields: Example: ` flashduty channel escalate-rule-update --data '{"channel_id":1001,"layers":[{"target":{"by":{"critical":["voice"],"warning":["sms"]},"person_ids":[42]}}],"rule_id":"6621b23f4a2c5e0012ab34d0","rule_name":"Default escalation","template_id":"6621b23f4a2c5e0012ab34d1"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("aggr-window") { body["aggr_window"] = fAggrWindow } @@ -796,6 +822,7 @@ Request fields: if cmd.Flags().Changed("template-id") { body["template_id"] = fTemplateID } + return nil }) if err != nil { return err @@ -823,7 +850,7 @@ Request fields: cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Escalation rule ID (MongoDB ObjectID). (required)") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Notification template ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -831,7 +858,7 @@ func genChannelsChannelInfoCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get channel detail", Long: `Get channel detail. @@ -884,13 +911,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - team_id (integer) — Owning team ID. - updated_at (integer) — Last update timestamp (unix seconds). `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel info --data '{"channel_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -908,7 +940,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel ID to fetch. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -916,7 +948,7 @@ func genChannelsChannelInfosCmd() *cobra.Command { var dataJSON string var fChannelIDs []int cmd := &cobra.Command{ - Use: "infos", + Use: "infos [...]", Short: "Batch get channels", Long: `Batch get channels. @@ -933,13 +965,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - channel_name (string) (required) — Channel name. - status (string) — Channel status. [enabled, disabled] `, + Args: requireArgs("channel_ids"), Example: ` flashduty channel infos --data '{"channel_ids":[1001,1002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("channel-ids") { body["channel_ids"] = fChannelIDs } + return nil }) if err != nil { return err @@ -957,7 +994,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fChannelIDs, "channel-ids", nil, "Channel IDs to look up. Up to 1000. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -970,7 +1007,7 @@ func genChannelsChannelInhibitRuleCreateCmd() *cobra.Command { var fPriority int64 var fRuleName string cmd := &cobra.Command{ - Use: "inhibit-rule-create", + Use: "inhibit-rule-create ", Short: "Create inhibit rule", Long: `Create inhibit rule. @@ -992,10 +1029,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). - rule_name (string) (required) — Rule name echoed back from the request. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel inhibit-rule-create --data '{"channel_id":3521074710131,"description":"When a Critical alert fires, suppress matching Info alerts","equals":["labels.cluster","labels.service"],"is_directly_discard":false,"rule_name":"Suppress Info when Critical fires","source_filters":[[{"key":"severity","oper":"IN","vals":["Critical"]}]],"target_filters":[[{"key":"severity","oper":"IN","vals":["Info"]}]]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -1014,6 +1055,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("rule-name") { body["rule_name"] = fRuleName } + return nil }) if err != nil { return err @@ -1036,7 +1078,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fIsDirectlyDiscard, "is-directly-discard", false, "When true, suppressed target alerts are dropped instead of merged.") cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1060,13 +1102,14 @@ Request fields: Example: ` flashduty channel inhibit-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -1089,7 +1132,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1113,13 +1156,14 @@ Request fields: Example: ` flashduty channel inhibit-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -1142,7 +1186,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1166,13 +1210,14 @@ Request fields: Example: ` flashduty channel inhibit-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -1195,7 +1240,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1203,7 +1248,7 @@ func genChannelsChannelInhibitRuleListCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "inhibit-rule-list", + Use: "inhibit-rule-list ", Short: "List inhibit rules", Long: `List inhibit rules. @@ -1232,13 +1277,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) - updated_by (integer) (required) `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel inhibit-rule-list --data '{"channel_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -1256,7 +1306,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1292,7 +1342,7 @@ Request fields: Example: ` flashduty channel inhibit-rule-update --data '{"channel_id":1001,"equals":["labels.cluster"],"rule_id":"6621b23f4a2c5e0012ab34ce","rule_name":"Suppress downstream"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -1314,6 +1364,7 @@ Request fields: if cmd.Flags().Changed("rule-name") { body["rule_name"] = fRuleName } + return nil }) if err != nil { return err @@ -1341,7 +1392,7 @@ Request fields: cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Inhibit rule ID (MongoDB ObjectID). (required)") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1432,7 +1483,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty channel list --data '{"asc":false,"limit":20,"orderby":"created_at","p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -1472,6 +1523,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -1501,7 +1553,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Field used to order results. [ranking, created_at, updated_at, channel_name, last_incident_at]") cmd.Flags().StringVar(&fQuery, "query", "", "Free-text query against channel name/description.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1515,7 +1567,7 @@ func genChannelsChannelSilenceRuleCreateCmd() *cobra.Command { var fPriority int64 var fRuleName string cmd := &cobra.Command{ - Use: "silence-rule-create", + Use: "silence-rule-create ", Short: "Create silence rule", Long: `Create silence rule. @@ -1546,10 +1598,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). - rule_name (string) (required) — Rule name echoed back from the request. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel silence-rule-create --data '{"channel_id":3521074710131,"description":"Silence all Info alerts during planned maintenance","filters":[[{"key":"severity","oper":"IN","vals":["Info"]}]],"is_directly_discard":false,"rule_name":"Maintenance window silence","time_filter":{"end_time":1773414000,"start_time":1773388800}}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -1571,6 +1627,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("rule-name") { body["rule_name"] = fRuleName } + return nil }) if err != nil { return err @@ -1594,7 +1651,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fIsDirectlyDiscard, "is-directly-discard", false, "When true, silenced alerts are dropped instead of suppressed into incidents.") cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1618,13 +1675,14 @@ Request fields: Example: ` flashduty channel silence-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -1647,7 +1705,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1671,13 +1729,14 @@ Request fields: Example: ` flashduty channel silence-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -1700,7 +1759,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1724,13 +1783,14 @@ Request fields: Example: ` flashduty channel silence-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -1753,7 +1813,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1761,7 +1821,7 @@ func genChannelsChannelSilenceRuleListCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "silence-rule-list", + Use: "silence-rule-list ", Short: "List silence rules", Long: `List silence rules. @@ -1800,13 +1860,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) - updated_by (integer) (required) `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel silence-rule-list --data '{"channel_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -1824,7 +1889,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1868,7 +1933,7 @@ Request fields: Example: ` flashduty channel silence-rule-update --data '{"channel_id":1001,"filters":[[{"key":"labels.service","oper":"IN","vals":["billing"]}]],"rule_id":"6621b23f4a2c5e0012ab34cd","rule_name":"Mute during maintenance","time_filter":{"end_time":1710086400,"start_time":1710000000}}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -1890,6 +1955,7 @@ Request fields: if cmd.Flags().Changed("rule-name") { body["rule_name"] = fRuleName } + return nil }) if err != nil { return err @@ -1917,7 +1983,7 @@ Request fields: cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Silence rule ID (MongoDB ObjectID). (required)") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1928,7 +1994,7 @@ func genChannelsChannelUnsubscribeRuleCreateCmd() *cobra.Command { var fPriority int64 var fRuleName string cmd := &cobra.Command{ - Use: "unsubscribe-rule-create", + Use: "unsubscribe-rule-create ", Short: "Create drop rule", Long: `Create drop rule. @@ -1947,10 +2013,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - rule_id (string) (required) — Newly created rule ID (MongoDB ObjectID). - rule_name (string) (required) — Rule name echoed back from the request. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel unsubscribe-rule-create --data '{"channel_id":3521074710131,"description":"Discard all alerts from the test environment before they create incidents","filters":[[{"key":"labels.env","oper":"IN","vals":["test","dev"]}]],"rule_name":"Drop test environment alerts"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -1963,6 +2033,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("rule-name") { body["rule_name"] = fRuleName } + return nil }) if err != nil { return err @@ -1983,7 +2054,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fDescription, "description", "", "Rule description, up to 500 characters. (≤500 chars)") cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2007,13 +2078,14 @@ Request fields: Example: ` flashduty channel unsubscribe-rule-delete --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -2036,7 +2108,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2060,13 +2132,14 @@ Request fields: Example: ` flashduty channel unsubscribe-rule-disable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -2089,7 +2162,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2113,13 +2186,14 @@ Request fields: Example: ` flashduty channel unsubscribe-rule-enable --data '{"channel_id":3521074710131,"rule_id":"6621b23f4a2c5e0012ab34cd"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } if cmd.Flags().Changed("rule-id") { body["rule_id"] = fRuleID } + return nil }) if err != nil { return err @@ -2142,7 +2216,7 @@ Request fields: } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel the rule belongs to. (required)") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Rule ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2150,7 +2224,7 @@ func genChannelsChannelUnsubscribeRuleListCmd() *cobra.Command { var dataJSON string var fChannelID int64 cmd := &cobra.Command{ - Use: "unsubscribe-rule-list", + Use: "unsubscribe-rule-list ", Short: "List drop rules", Long: `List drop rules. @@ -2176,13 +2250,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) - updated_by (integer) (required) `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel unsubscribe-rule-list --data '{"channel_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } + return nil }) if err != nil { return err @@ -2200,7 +2279,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().Int64Var(&fChannelID, "channel-id", 0, "Channel to list rules for. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2231,7 +2310,7 @@ Request fields: Example: ` flashduty channel unsubscribe-rule-update --data '{"channel_id":1001,"filters":[[{"key":"labels.env","oper":"IN","vals":["test"]}]],"rule_id":"6621b23f4a2c5e0012ab34cf","rule_name":"Drop test alerts"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -2247,6 +2326,7 @@ Request fields: if cmd.Flags().Changed("rule-name") { body["rule_name"] = fRuleName } + return nil }) if err != nil { return err @@ -2272,7 +2352,7 @@ Request fields: cmd.Flags().Int64Var(&fPriority, "priority", 0, "Evaluation priority. Lower runs first.") cmd.Flags().StringVar(&fRuleID, "rule-id", "", "Drop rule ID (MongoDB ObjectID). (required)") cmd.Flags().StringVar(&fRuleName, "rule-name", "", "Rule name, 1 to 39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2290,7 +2370,7 @@ func genChannelsChannelUpdateCmd() *cobra.Command { var fManagingTeamIDs []int var fTeamID int64 cmd := &cobra.Command{ - Use: "update", + Use: "update ", Short: "Update channel", Long: `Update channel. @@ -2330,10 +2410,14 @@ Request fields: Response fields ('data' envelope is unwrapped — these fields are at the top level): - external_report_token (string) — Newly generated token for external reporters. Only returned when 'is_external_report_enabled' is set to 'true' in the request. Callers should store this value; it cannot be retrieved afterwards. `, + Args: requireExactArg("channel_id"), Example: ` flashduty channel update --data '{"channel_id":1001,"channel_name":"Production Alerts (v2)","description":"Updated description"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "channel_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("auto-resolve-mode") { body["auto_resolve_mode"] = fAutoResolveMode } @@ -2367,6 +2451,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -2394,7 +2479,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fIsPrivate, "is-private", false, "When true, the channel is visible only to its managing teams.") cmd.Flags().IntSliceVar(&fManagingTeamIDs, "managing-team-ids", nil, "Additional teams that can manage the channel. Up to 3 entries.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "New owning team ID.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2402,7 +2487,7 @@ func genChannelsRouteInfoCmd() *cobra.Command { var dataJSON string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get routing rule detail", Long: `Get routing rule detail. @@ -2437,13 +2522,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_by (integer) (required) — ID of the person who performed the last update. - version (integer) (required) — Monotonic version number, incremented on each update. Use it for optimistic concurrency control. `, + Args: requireExactArg("integration_id"), Example: ` flashduty route info --data '{"integration_id":6113996590131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -2461,7 +2551,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID. Must be greater than 0. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2469,7 +2559,7 @@ func genChannelsRouteListCmd() *cobra.Command { var dataJSON string var fIntegrationIDs []int cmd := &cobra.Command{ - Use: "list", + Use: "list [...]", Short: "List routing rules", Long: `List routing rules. @@ -2505,13 +2595,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_by (integer) (required) — ID of the person who performed the last update. - version (integer) (required) — Monotonic version number, incremented on each update. Use it for optimistic concurrency control. `, + Args: requireArgs("integration_ids"), Example: ` flashduty route list --data '{"integration_ids":[6113996590131,6113996590132]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("integration-ids") { body["integration_ids"] = fIntegrationIDs } + return nil }) if err != nil { return err @@ -2529,7 +2624,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fIntegrationIDs, "integration-ids", nil, "Integration IDs to fetch routing rules for. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2538,7 +2633,7 @@ func genChannelsRouteUpsertCmd() *cobra.Command { var fIntegrationID int64 var fVersion int64 cmd := &cobra.Command{ - Use: "upsert", + Use: "upsert ", Short: "Upsert routing rule", Long: `Upsert routing rule. @@ -2564,16 +2659,21 @@ Request fields: - name (string) (required) — Section name. Must be unique within the rule. - position (integer) (required) — Index in 'cases' where this section starts. Must be between 0 and the length of 'cases'. `, + Args: requireExactArg("integration_id"), Example: ` flashduty route upsert --data '{"cases":[{"channel_ids":[3521074710131],"fallthrough":false,"if":[{"key":"severity","oper":"IN","vals":["Critical"]}],"routing_mode":"standard"}],"default":{"channel_ids":[3521074710131]},"integration_id":6113996590131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "integration_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } if cmd.Flags().Changed("version") { body["version"] = fVersion } + return nil }) if err != nil { return err @@ -2596,7 +2696,7 @@ Request fields: } cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration the rule belongs to. (required)") cmd.Flags().Int64Var(&fVersion, "version", 0, "Expected current version for optimistic concurrency control. Pass the value returned by the latest read.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_data_sources.go b/internal/cli/zz_generated_data_sources.go index ea08d34..77de8ba 100644 --- a/internal/cli/zz_generated_data_sources.go +++ b/internal/cli/zz_generated_data_sources.go @@ -147,10 +147,11 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit datasource-info --data '{"id":10}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -168,7 +169,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -311,10 +312,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit datasource-list --data '{"type":"prometheus"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("type") { body["type"] = fType } + return nil }) if err != nil { return err @@ -332,7 +334,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().StringVar(&fType, "type", "", "Filter by datasource type identifier. Omit to return all types. Allowed values: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -360,7 +362,7 @@ Request fields: Example: ` flashduty monit datasource-sls-logstores --data '{"id":10,"offset":0,"project":"project-a","size":50}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } @@ -373,6 +375,7 @@ Request fields: if cmd.Flags().Changed("size") { body["size"] = fSize } + return nil }) if err != nil { return err @@ -393,7 +396,7 @@ Request fields: cmd.Flags().Int64Var(&fOffset, "offset", 0, "Pagination offset.") cmd.Flags().StringVar(&fProject, "project", "", "SLS project name.") cmd.Flags().Int64Var(&fSize, "size", 0, "Page size.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -421,7 +424,7 @@ Request fields: Example: ` flashduty monit datasource-sls-projects --data '{"id":10,"offset":0,"query":"","size":50}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } @@ -434,6 +437,7 @@ Request fields: if cmd.Flags().Changed("size") { body["size"] = fSize } + return nil }) if err != nil { return err @@ -454,7 +458,7 @@ Request fields: cmd.Flags().Int64Var(&fOffset, "offset", 0, "Pagination offset.") cmd.Flags().StringVar(&fQuery, "query", "", "Name prefix filter.") cmd.Flags().Int64Var(&fSize, "size", 0, "Page size.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -717,7 +721,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit datasource-create --data '{"address":"http://prometheus.example.com:9090","edge_cluster_name":"default","name":"Prometheus Prod","note":"Production Prometheus","payload":{"prometheus":{"basic_auth_enabled":false}},"type_ident":"prometheus"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("address") { body["address"] = fAddress } @@ -736,6 +740,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("type-ident") { body["type_ident"] = fTypeIdent } + return nil }) if err != nil { return err @@ -758,7 +763,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. (required)") cmd.Flags().StringVar(&fNote, "note", "", "Optional description.") cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -780,10 +785,11 @@ Request fields: Example: ` flashduty monit datasource-delete --data '{"id":10}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -805,7 +811,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1068,7 +1074,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit datasource-update --data '{"address":"http://prometheus-v2.example.com:9090","edge_cluster_name":"default","id":10,"name":"Prometheus Prod v2","note":"Updated","payload":{"prometheus":{"basic_auth_enabled":false}},"type_ident":"prometheus"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("address") { body["address"] = fAddress } @@ -1087,6 +1093,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("type-ident") { body["type_ident"] = fTypeIdent } + return nil }) if err != nil { return err @@ -1109,7 +1116,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fName, "name", "", "Datasource display name. (required)") cmd.Flags().StringVar(&fNote, "note", "", "Optional description.") cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier. Allowed: 'prometheus', 'loki', 'mysql', 'oracle', 'postgres', 'clickhouse', 'elasticsearch', 'sls', 'victorialogs'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_diagnostics.go b/internal/cli/zz_generated_diagnostics.go index e8314a0..b638a90 100644 --- a/internal/cli/zz_generated_diagnostics.go +++ b/internal/cli/zz_generated_diagnostics.go @@ -70,7 +70,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit query-diagnose --data '{"account_id":10001,"ds_name":"vmlogs-read","ds_type":"victorialogs","input":{"query":"_stream:{status='\''500'\''}"},"methods":[{"name":"pattern_snapshot"},{"baseline":"same_window_yesterday","name":"pattern_compare"}],"operation":"log_patterns","options":{"examples_per_pattern":2,"max_logs_scanned":10000,"max_patterns":20,"timeout_seconds":25},"time_range":{"end":1776849344,"start":1776847544}}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -83,6 +83,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("operation") { body["operation"] = fOperation } + return nil }) if err != nil { return err @@ -103,7 +104,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fDsName, "ds-name", "", "Data source name configured under the tenant. (required)") cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type. 'log_patterns' supports 'loki' and 'victorialogs'; 'metric_trends' supports 'prometheus'. (required)") cmd.Flags().StringVar(&fOperation, "operation", "", "Diagnostic operation. When omitted, inferred from 'ds_type' (loki / victorialogs → 'log_patterns', prometheus → 'metric_trends'). Other sources must specify explicitly. [log_patterns, metric_trends]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -138,7 +139,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit query-rows --data '{"account_id":10001,"delay_seconds":30,"ds_name":"prod-prom","ds_type":"prometheus","expr":"up"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -154,6 +155,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' if cmd.Flags().Changed("expr") { body["expr"] = fExpr } + return nil }) if err != nil { return err @@ -175,7 +177,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' cmd.Flags().StringVar(&fDsName, "ds-name", "", "Data source name; must match a configured data source under the tenant. (required)") cmd.Flags().StringVar(&fDsType, "ds-type", "", "Data source type; must match a configured data source under the tenant. Examples: 'prometheus', 'loki', 'victorialogs', 'sls', 'elasticsearch', 'mysql', 'postgres', 'oracle', 'clickhouse'. (required)") cmd.Flags().StringVar(&fExpr, "expr", "", "Query expression. Syntax depends on 'ds_type' and is interpreted by the corresponding monit-edge client (PromQL for Prometheus, LogQL for Loki, SQL for SQL sources, etc.). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -214,7 +216,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty monit targets --data '{"keyword":"db-prod","limit":50}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -227,6 +229,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("limit") { body["limit"] = fLimit } + return nil }) if err != nil { return err @@ -247,7 +250,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fCursor, "cursor", "", "Opaque pagination cursor from the previous response's 'next_cursor'. Omit / pass empty string for the first page. Reset whenever 'keyword', 'limit', or tenant changes.") cmd.Flags().StringVar(&fKeyword, "keyword", "", "Prefix match against 'target_locator'. ASCII only, no whitespace, no '|', max 256 bytes. Substring search is not supported.") cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size. Default 50, max 200. (max 200)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -290,7 +293,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit tools-catalog --data '{"account_id":10001,"include_output_shape":true,"target_locator":"web-01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -303,6 +306,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("target-locator") { body["target_locator"] = fTargetLocator } + return nil }) if err != nil { return err @@ -323,7 +327,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fIncludeOutputShape, "include-output-shape", false, "When true, each tool entry includes its 'output_shape' JSON Schema. Defaults to false to keep responses small for LLM consumption.") cmd.Flags().StringVar(&fTargetKind, "target-kind", "", "Optional target kind. When omitted webapi auto-infers across currently known kinds. Built-in kinds: 'host', 'mysql'. Required on retry when the previous call returned 'ambiguous_target_kind'.") cmd.Flags().StringVar(&fTargetLocator, "target-locator", "", "Target identifier (host name, MySQL address, …). Max 256 bytes; no whitespace, control characters, or '|'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -370,7 +374,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit tools-invoke --data '{"account_id":10001,"target_locator":"web-01","tools":[{"params":{},"tool":"os.overview"},{"params":{"host":"10.0.0.10","port":3306},"tool":"net.tcp_ping"}]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("account-id") { body["account_id"] = fAccountID } @@ -380,6 +384,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("target-locator") { body["target_locator"] = fTargetLocator } + return nil }) if err != nil { return err @@ -399,7 +404,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fAccountID, "account-id", 0, "Optional consistency check. Must equal the authenticated account when supplied.") cmd.Flags().StringVar(&fTargetKind, "target-kind", "", "Optional target kind; auto-inferred when omitted.") cmd.Flags().StringVar(&fTargetLocator, "target-locator", "", "Target identifier. Same validation rules as '/monit/tools/catalog'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_im_integrations.go b/internal/cli/zz_generated_im_integrations.go index dc8098e..f5391a2 100644 --- a/internal/cli/zz_generated_im_integrations.go +++ b/internal/cli/zz_generated_im_integrations.go @@ -42,7 +42,8 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty datasource im-war-room-enabled-list --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -56,7 +57,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_incidents.go b/internal/cli/zz_generated_incidents.go index 8edc0f0..29b5c54 100644 --- a/internal/cli/zz_generated_incidents.go +++ b/internal/cli/zz_generated_incidents.go @@ -12,7 +12,7 @@ func genIncidentsReadGetWarRoomDefaultObserversCmd() *cobra.Command { var dataJSON string var fIncidentID string cmd := &cobra.Command{ - Use: "war-room-default-observers", + Use: "war-room-default-observers ", Short: "Get war-room default observers", Long: `Get war-room default observers. @@ -36,13 +36,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - status (string) — Current status of the person. - time_zone (string) — Time zone of the person. `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident war-room-default-observers --data '{"incident_id":"664a1b2c3d4e5f6a7b8c9d0e"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } + return nil }) if err != nil { return err @@ -60,7 +65,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID, a MongoDB ObjectID hex string. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -70,7 +75,7 @@ func genIncidentsWriteAddWarRoomMemberCmd() *cobra.Command { var fIntegrationID int64 var fMemberIDs []int cmd := &cobra.Command{ - Use: "war-room-add-member", + Use: "war-room-add-member ", Short: "Add war-room member", Long: `Add war-room member. @@ -83,10 +88,14 @@ Request fields: --integration-id int (required) — IM integration that hosts the war room. --member-ids []int (required) — Person IDs to add to the war room. `, + Args: requireExactArg("chat_id"), Example: ` flashduty incident war-room-add-member --data '{"chat_id":"oc_5ce6d572455d361153b7cb51da133945","integration_id":362,"member_ids":[20001,20002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "chat_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("chat-id") { body["chat_id"] = fChatID } @@ -96,6 +105,7 @@ Request fields: if cmd.Flags().Changed("member-ids") { body["member_ids"] = fMemberIDs } + return nil }) if err != nil { return err @@ -115,7 +125,7 @@ Request fields: cmd.Flags().StringVar(&fChatID, "chat-id", "", "Chat ID of the war room within the IM platform. (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration that hosts the war room. (required)") cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Person IDs to add to the war room. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -123,7 +133,7 @@ func genIncidentsAckCmd() *cobra.Command { var dataJSON string var fIncidentIDs []string cmd := &cobra.Command{ - Use: "ack", + Use: "ack [...]", Short: "Acknowledge incident", Long: `Acknowledge incident. @@ -134,13 +144,18 @@ API: POST /incident/ack (incidentAck) Request fields: --incident-ids []string (required) — Incident IDs to acknowledge. At most 100 per call. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident ack --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -162,7 +177,7 @@ Request fields: }, } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to acknowledge. At most 100 per call. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -175,7 +190,7 @@ func genIncidentsAlertListCmd() *cobra.Command { var fIncludeEvents bool var fIsActive bool cmd := &cobra.Command{ - Use: "alert-list", + Use: "alert-list ", Short: "List alerts of incident", Long: `List alerts of incident. @@ -256,10 +271,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - updated_at (integer) (required) — Last update timestamp (seconds). - total (integer) (required) — Total matching alerts. `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident alert-list --data '{"incident_id":"69da451ef77b1b51f40e83ee","is_active":true,"limit":100,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("page") { body["p"] = fP } @@ -278,6 +297,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("is-active") { body["is_active"] = fIsActive } + return nil }) if err != nil { return err @@ -300,7 +320,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().BoolVar(&fIncludeEvents, "include-events", false, "When true, include raw alert events in each alert item.") cmd.Flags().BoolVar(&fIsActive, "is-active", false, "When true return only active alerts (Critical/Warning/Info); when false return only recovered alerts (Ok). Omit to include all.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -333,13 +353,14 @@ Request fields: Example: ` flashduty incident assign --data '{"assigned_to":{"person_ids":[2476444212131],"type":"assign"},"incident_id":"69da451ef77b1b51f40e83ee"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -362,7 +383,7 @@ Request fields: } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Single incident ID. Ignored when 'incident_ids' is also provided.") cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Batch incident IDs.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -372,7 +393,7 @@ func genIncidentsCommentCmd() *cobra.Command { var fIncidentIDs []string var fMuteReply bool cmd := &cobra.Command{ - Use: "comment", + Use: "comment [...]", Short: "Add comment to incident", Long: `Add comment to incident. @@ -385,10 +406,14 @@ Request fields: --incident-ids []string (required) — Incident IDs to comment on. At most 100 per call. --mute-reply bool — When true, do not trigger webhook reply actions for this comment. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident comment --data '{"comment":"Identified the root cause. Rolling back the deployment now.","incident_ids":["69da451ef77b1b51f40e83ee"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("comment") { body["comment"] = fComment } @@ -398,6 +423,7 @@ Request fields: if cmd.Flags().Changed("mute-reply") { body["mute_reply"] = fMuteReply } + return nil }) if err != nil { return err @@ -421,7 +447,7 @@ Request fields: cmd.Flags().StringVar(&fComment, "comment", "", "Comment body. (≤1024 chars)") cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to comment on. At most 100 per call. (required)") cmd.Flags().BoolVar(&fMuteReply, "mute-reply", false, "When true, do not trigger webhook reply actions for this comment.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -463,7 +489,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty incident create --data '{"assigned_to":{"person_ids":[2476444212131]},"channel_id":2551105804131,"incident_severity":"Critical","title":"Database connection timeout on prod-db-01"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("channel-id") { body["channel_id"] = fChannelID } @@ -476,6 +502,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("title") { body["title"] = fTitle } + return nil }) if err != nil { return err @@ -496,7 +523,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fDescription, "description", "", "Incident description, up to 1024 characters. (≤1024 chars)") cmd.Flags().StringVar(&fIncidentSeverity, "incident-severity", "", "Incident severity. (required) [Info, Warning, Critical]") cmd.Flags().StringVar(&fTitle, "title", "", "Incident title, up to 512 characters. (≤512 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -523,13 +550,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty incident custom-action-do --data '{"incident_id":"69da451ef77b1b51f40e83ee","integration_id":2490562293131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -548,7 +576,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Custom action integration ID. Must be enabled and associated with the incident's channel. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -556,7 +584,7 @@ func genIncidentsDisableMergeCmd() *cobra.Command { var dataJSON string var fIncidentIDs []string cmd := &cobra.Command{ - Use: "disable-merge", + Use: "disable-merge [...]", Short: "Disable incident merge", Long: `Disable incident merge. @@ -567,13 +595,18 @@ API: POST /incident/disable-merge (incidentDisableMerge) Request fields: --incident-ids []string (required) — Incident IDs whose automatic merge should be disabled. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident disable-merge --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -595,7 +628,7 @@ Request fields: }, } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs whose automatic merge should be disabled. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -608,7 +641,7 @@ func genIncidentsFeedCmd() *cobra.Command { var fIncidentID string var fTypes []string cmd := &cobra.Command{ - Use: "feed", + Use: "feed ", Short: "Get incident timeline", Long: `Get incident timeline. @@ -636,10 +669,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - type (string) (required) — Incident timeline entry type. Each value identifies one lifecycle event; the matching 'detail' payload shape is determined by this field. Incident types are prefixed with 'i_'. | Type | Meaning | |---|---| | 'i_new' | Incident Created: A new incident was created automatically or manually. | | 'i_assign' | Assigned: Incident was assigned to responders. | | 'i_a_rspd' | Responder Added: Additional responders joined the incident. | | 'i_notify' | Notification dispatched through a channel at a specific escalation level. | | 'i_storm' | Alert storm threshold reached on the incident. | | 'i_snooze' | Notifications snoozed for a given duration. | | 'i_wake' | Snooze cancelled and notifications resumed. | | 'i_ack' | Acknowledged: Responder confirmed they are working on the incident. | | 'i_unack' | Acknowledgement removed. | | 'i_comm' | Comment: Responder logged progress or key information. | | 'i_rslv' | Resolved: Incident was marked as resolved. | | 'i_reopen' | Reopened: Resolved incident was reopened, possibly due to recurrence. | | 'i_merge' | Merged: Multiple related incidents were merged into one. | | 'i_r_title' | Title updated. | | 'i_r_desc' | Description updated. | | 'i_r_impact' | Impact updated. | | 'i_r_rc' | Root cause updated. | | 'i_r_rsltn' | Resolution updated. | | 'i_r_severity' | Severity Changed: Incident severity level was adjusted. | | 'i_r_field' | Custom field value updated. | | 'i_m_flapping' | Incident muted by flapping detection. | | 'i_m_reply' | Mute reply marker on a comment. | | 'i_custom' | Action: Automated action or script was triggered. | | 'i_wr_create' | War Room Created: Chat group was created for collaborative response. | | 'i_wr_delete' | War room chat group deleted. | | 'i_auto_refresh' | Card auto-refresh event posted back to the timeline. | | 'a_merge' | Alert Merged: An alert was merged into an existing incident. | [i_new, i_assign, i_a_rspd, i_notify, i_storm, i_snooze, i_wake, i_ack, i_unack, i_comm, i_rslv, i_reopen, i_merge, i_r_title, i_r_desc, i_r_impact, i_r_rc, i_r_rsltn, i_r_severity, i_r_field, i_m_flapping, i_m_reply, i_custom, i_wr_create, i_wr_delete, i_auto_refresh, a_merge] - updated_at (integer) (required) — Last update timestamp in milliseconds. `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident feed --data '{"incident_id":"69da451ef77b1b51f40e83ee","limit":20,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("page") { body["p"] = fP } @@ -658,6 +695,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("types") { body["types"] = fTypes } + return nil }) if err != nil { return err @@ -680,7 +718,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending chronological order when true.") cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().StringSliceVar(&fTypes, "types", nil, "Optional filter restricting the returned entries to specific types. [i_new, i_assign, i_a_rspd, i_notify, i_storm, i_snooze, i_wake, i_ack, i_unack, i_comm, i_rslv, i_reopen, i_merge, i_r_title, i_r_desc, i_r_impact, i_r_rc, i_r_rsltn, i_r_severity, i_r_field, i_m_flapping, i_m_reply, i_custom, i_wr_create, i_wr_delete, i_auto_refresh, a_merge]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -689,7 +727,7 @@ func genIncidentsFieldResetCmd() *cobra.Command { var fFieldName string var fIncidentID string cmd := &cobra.Command{ - Use: "field-reset", + Use: "field-reset ", Short: "Update incident custom field", Long: `Update incident custom field. @@ -702,16 +740,21 @@ Request fields: --incident-id string (required) — Incident ID (MongoDB ObjectID). field_value (any, via --data) — New field value. Type must match the field definition. `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident field-reset --data '{"field_name":"affected_service","field_value":"payment-service","incident_id":"69da451ef77b1b51f40e83ee"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("field-name") { body["field_name"] = fFieldName } if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } + return nil }) if err != nil { return err @@ -734,7 +777,7 @@ Request fields: } cmd.Flags().StringVar(&fFieldName, "field-name", "", "Custom field name; must match a field defined on the account. (required)") cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -742,7 +785,7 @@ func genIncidentsInfoCmd() *cobra.Command { var dataJSON string var fIncidentID string cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get incident detail", Long: `Get incident detail. @@ -909,13 +952,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - title (string) (required) — Incident title. - updated_at (integer) (required) — Last update timestamp (seconds). `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident info --data '{"incident_id":"69da451ef77b1b51f40e83ee"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } + return nil }) if err != nil { return err @@ -933,7 +981,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1162,7 +1210,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -1229,6 +1277,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -1267,7 +1316,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().IntSliceVar(&fResponderIDs, "responder-ids", nil, "Responder member IDs.") cmd.Flags().StringVar(&fStartTime, "start-time", "", "Window start, Unix seconds. (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Team IDs; resolved to channels via channel ownership.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1275,7 +1324,7 @@ func genIncidentsListByIDsCmd() *cobra.Command { var dataJSON string var fIncidentIDs []string cmd := &cobra.Command{ - Use: "list-by-ids", + Use: "list-by-ids [...]", Short: "List incidents by IDs", Long: `List incidents by IDs. @@ -1443,13 +1492,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - search_after_ctx (string) — Opaque cursor to pass as 'search_after_ctx' on the next request. - total (integer) (required) — Total number of matching incidents. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident list-by-ids --data '{"incident_ids":["69da451ef77b1b51f40e83ee","69da451ef77b1b51f40e83ef"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -1467,7 +1521,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to fetch. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1480,7 +1534,7 @@ func genIncidentsMergeCmd() *cobra.Command { var fTargetIncidentID string var fTitle string cmd := &cobra.Command{ - Use: "merge", + Use: "merge ", Short: "Merge incidents", Long: `Merge incidents. @@ -1496,10 +1550,14 @@ Request fields: --target-incident-id string (required) — Target incident ID that source incidents will be merged into. --title string — Optional new title for the target incident. (≤512 chars) `, + Args: requireExactArg("target_incident_id"), Example: ` flashduty incident merge --data '{"comment":"Merging related database connectivity incidents into one.","source_incident_ids":["69da451ef77b1b51f40e83ef","69da451ef77b1b51f40e83f0"],"target_incident_id":"69da451ef77b1b51f40e83ee"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "target_incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("comment") { body["comment"] = fComment } @@ -1518,6 +1576,7 @@ Request fields: if cmd.Flags().Changed("title") { body["title"] = fTitle } + return nil }) if err != nil { return err @@ -1544,7 +1603,7 @@ Request fields: cmd.Flags().StringSliceVar(&fSourceIncidentIDs, "source-incident-ids", nil, "Source incident IDs. The target incident is removed from this set automatically. (required)") cmd.Flags().StringVar(&fTargetIncidentID, "target-incident-id", "", "Target incident ID that source incidents will be merged into. (required)") cmd.Flags().StringVar(&fTitle, "title", "", "Optional new title for the target incident. (≤512 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1553,7 +1612,7 @@ func genIncidentsPastListCmd() *cobra.Command { var fIncidentID string var fLimit int64 cmd := &cobra.Command{ - Use: "past-list", + Use: "past-list ", Short: "List past incidents", Long: `List past incidents. @@ -1720,16 +1779,21 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - title (string) (required) — Incident title. - updated_at (integer) (required) — Last update timestamp (seconds). `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident past-list --data '{"incident_id":"69da451ef77b1b51f40e83ee","limit":5}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } if cmd.Flags().Changed("limit") { body["limit"] = fLimit } + return nil }) if err != nil { return err @@ -1748,7 +1812,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Reference incident ID (MongoDB ObjectID). (required)") cmd.Flags().Int64Var(&fLimit, "limit", 0, "Maximum number of similar incidents to return. (0-100)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1756,7 +1820,7 @@ func genIncidentsPostMortemDeleteCmd() *cobra.Command { var dataJSON string var fPostMortemID string cmd := &cobra.Command{ - Use: "post-mortem-delete", + Use: "post-mortem-delete ", Short: "Delete post-mortem", Long: `Delete post-mortem. @@ -1767,13 +1831,18 @@ API: POST /incident/post-mortem/delete (incidentPostMortemDelete) Request fields: --post-mortem-id string (required) — Post-mortem ID. `, + Args: requireExactArg("post_mortem_id"), Example: ` flashduty incident post-mortem-delete --data '{"post_mortem_id":"8104935102bf89dc01ac638a5261fe7e"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "post_mortem_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("post-mortem-id") { body["post_mortem_id"] = fPostMortemID } + return nil }) if err != nil { return err @@ -1795,7 +1864,7 @@ Request fields: }, } cmd.Flags().StringVar(&fPostMortemID, "post-mortem-id", "", "Post-mortem ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1803,7 +1872,7 @@ func genIncidentsPostMortemInfoCmd() *cobra.Command { var dataJSON string var fPostMortemID string cmd := &cobra.Command{ - Use: "post-mortem-info", + Use: "post-mortem-info ", Short: "Get post-mortem", Long: `Get post-mortem. @@ -1846,12 +1915,17 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - title (string) (required) — Report title. - updated_at_seconds (integer) (required) — Last update timestamp (seconds). `, + Args: requireExactArg("post_mortem_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "post_mortem_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("post-mortem-id") { body["post_mortem_id"] = fPostMortemID } + return nil }) if err != nil { return err @@ -1869,7 +1943,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fPostMortemID, "post-mortem-id", "", "Post-mortem ID. Deterministic hash derived from account ID and the set of linked incident IDs. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1937,7 +2011,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -1968,6 +2042,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -1994,7 +2069,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderBy, "order-by", "", "Field used to order results. [created_at_seconds, updated_at_seconds]") cmd.Flags().StringVar(&fStatus, "status", "", "Report status. Defaults to 'published' on the server when omitted. [drafting, published]") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Team IDs to restrict the query to.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2002,7 +2077,7 @@ func genIncidentsRemoveCmd() *cobra.Command { var dataJSON string var fIncidentIDs []string cmd := &cobra.Command{ - Use: "remove", + Use: "remove [...]", Short: "Delete an incident", Long: `Delete an incident. @@ -2013,13 +2088,18 @@ API: POST /incident/remove (incidentRemove) Request fields: --incident-ids []string (required) — Incident IDs to remove. At most 100 per call. The caller must have access to every channel the incidents belong to. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident remove --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -2041,7 +2121,7 @@ Request fields: }, } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to remove. At most 100 per call. The caller must have access to every channel the incidents belong to. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2050,7 +2130,7 @@ func genIncidentsReopenCmd() *cobra.Command { var fIncidentIDs []string var fReason string cmd := &cobra.Command{ - Use: "reopen", + Use: "reopen [...]", Short: "Reopen incident", Long: `Reopen incident. @@ -2062,16 +2142,21 @@ Request fields: --incident-ids []string (required) — Incident IDs to reopen. At most 100 per call. --reason string — Optional reason recorded on the timeline. (≤1024 chars) `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident reopen --data '{"incident_ids":["69da451ef77b1b51f40e83ee"],"reason":"Monitoring detected the issue recurred after the initial fix."}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } if cmd.Flags().Changed("reason") { body["reason"] = fReason } + return nil }) if err != nil { return err @@ -2094,7 +2179,7 @@ Request fields: } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to reopen. At most 100 per call. (required)") cmd.Flags().StringVar(&fReason, "reason", "", "Optional reason recorded on the timeline. (≤1024 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2108,7 +2193,7 @@ func genIncidentsResetCmd() *cobra.Command { var fRootCause string var fTitle string cmd := &cobra.Command{ - Use: "reset", + Use: "reset ", Short: "Update incident fields", Long: `Update incident fields. @@ -2125,10 +2210,14 @@ Request fields: --root-cause string — New root cause analysis. (3-6144 chars) --title string — New incident title. (3-200 chars) `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident reset --data '{"incident_id":"69da451ef77b1b51f40e83ee","incident_severity":"Critical","title":"Database connection timeout - prod-db-01 primary"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -2150,6 +2239,7 @@ Request fields: if cmd.Flags().Changed("title") { body["title"] = fTitle } + return nil }) if err != nil { return err @@ -2177,7 +2267,7 @@ Request fields: cmd.Flags().StringVar(&fResolution, "resolution", "", "New resolution notes. (3-6144 chars)") cmd.Flags().StringVar(&fRootCause, "root-cause", "", "New root cause analysis. (3-6144 chars)") cmd.Flags().StringVar(&fTitle, "title", "", "New incident title. (3-200 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2187,7 +2277,7 @@ func genIncidentsResolveCmd() *cobra.Command { var fResolution string var fRootCause string cmd := &cobra.Command{ - Use: "resolve", + Use: "resolve [...]", Short: "Resolve incident", Long: `Resolve incident. @@ -2200,10 +2290,14 @@ Request fields: --resolution string — Optional resolution note applied to every resolved incident. (≤1024 chars) --root-cause string — Optional root cause note applied to every resolved incident. (≤1024 chars) `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident resolve --data '{"incident_ids":["69da451ef77b1b51f40e83ee"],"resolution":"Deployed hotfix v2.3.1 and restarted the affected service.","root_cause":"Memory leak in the connection pool caused by a missing cleanup call."}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } @@ -2213,6 +2307,7 @@ Request fields: if cmd.Flags().Changed("root-cause") { body["root_cause"] = fRootCause } + return nil }) if err != nil { return err @@ -2236,7 +2331,7 @@ Request fields: cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to resolve. At most 100 per call. (required)") cmd.Flags().StringVar(&fResolution, "resolution", "", "Optional resolution note applied to every resolved incident. (≤1024 chars)") cmd.Flags().StringVar(&fRootCause, "root-cause", "", "Optional root cause note applied to every resolved incident. (≤1024 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2245,7 +2340,7 @@ func genIncidentsResponderAddCmd() *cobra.Command { var fIncidentID string var fPersonIDs []int cmd := &cobra.Command{ - Use: "responder-add", + Use: "responder-add [...]", Short: "Add incident responder", Long: `Add incident responder. @@ -2261,16 +2356,21 @@ Request fields: - personal_channels (array) — Channels to use (e.g. 'voice', 'sms', 'email'). - template_id (string) — Notification template ID (MongoDB ObjectID). `, + Args: requireArgs("person_ids"), Example: ` flashduty incident responder-add --data '{"incident_id":"69da451ef77b1b51f40e83ee","person_ids":[2476444212131,2476444212132]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "person_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } if cmd.Flags().Changed("person-ids") { body["person_ids"] = fPersonIDs } + return nil }) if err != nil { return err @@ -2293,7 +2393,7 @@ Request fields: } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().IntSliceVar(&fPersonIDs, "person-ids", nil, "Member IDs to add as responders. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2302,7 +2402,7 @@ func genIncidentsSnoozeCmd() *cobra.Command { var fIncidentIDs []string var fMinutes int64 cmd := &cobra.Command{ - Use: "snooze", + Use: "snooze [...]", Short: "Snooze incident", Long: `Snooze incident. @@ -2314,16 +2414,21 @@ Request fields: --incident-ids []string (required) — Incident IDs to snooze. At most 100 per call. --minutes int (required) — Duration in minutes. Must be greater than 0 and at most 1440 (24h). (max 1440) `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident snooze --data '{"incident_ids":["69da451ef77b1b51f40e83ee"],"minutes":60}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } if cmd.Flags().Changed("minutes") { body["minutes"] = fMinutes } + return nil }) if err != nil { return err @@ -2346,7 +2451,7 @@ Request fields: } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to snooze. At most 100 per call. (required)") cmd.Flags().Int64Var(&fMinutes, "minutes", 0, "Duration in minutes. Must be greater than 0 and at most 1440 (24h). (required) (max 1440)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2354,7 +2459,7 @@ func genIncidentsUnackCmd() *cobra.Command { var dataJSON string var fIncidentIDs []string cmd := &cobra.Command{ - Use: "unack", + Use: "unack [...]", Short: "Unacknowledge incident", Long: `Unacknowledge incident. @@ -2365,13 +2470,18 @@ API: POST /incident/unack (incidentUnack) Request fields: --incident-ids []string (required) — Incident IDs to unacknowledge. At most 100 per call. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident unack --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -2393,7 +2503,7 @@ Request fields: }, } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to unacknowledge. At most 100 per call. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2401,7 +2511,7 @@ func genIncidentsWakeCmd() *cobra.Command { var dataJSON string var fIncidentIDs []string cmd := &cobra.Command{ - Use: "wake", + Use: "wake [...]", Short: "Wake incident", Long: `Wake incident. @@ -2412,13 +2522,18 @@ API: POST /incident/wake (incidentWake) Request fields: --incident-ids []string (required) — Incident IDs to wake. At most 100 per call. `, + Args: requireArgs("incident_ids"), Example: ` flashduty incident wake --data '{"incident_ids":["69da451ef77b1b51f40e83ee"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_ids", "slice"); err != nil { + return err + } if cmd.Flags().Changed("incident-ids") { body["incident_ids"] = fIncidentIDs } + return nil }) if err != nil { return err @@ -2440,7 +2555,7 @@ Request fields: }, } cmd.Flags().StringSliceVar(&fIncidentIDs, "incident-ids", nil, "Incident IDs to wake. At most 100 per call. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2473,7 +2588,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty incident war-room-create --data '{"add_observers":true,"incident_id":"69da451ef77b1b51f40e83ee","integration_id":2490562293131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("add-observers") { body["add_observers"] = fAddObservers } @@ -2486,6 +2601,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("member-ids") { body["member_ids"] = fMemberIDs } + return nil }) if err != nil { return err @@ -2506,7 +2622,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration ID. Must have war room enabled; Feishu, DingTalk, WeCom (self-built), Slack and Teams are supported. (required)") cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Additional member IDs to add to the war room.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2530,13 +2646,14 @@ Request fields: Example: ` flashduty incident war-room-delete --data '{"incident_id":"69da451ef77b1b51f40e83ee","integration_id":2490562293131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -2559,7 +2676,7 @@ Request fields: } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2568,7 +2685,7 @@ func genIncidentsWarRoomDetailCmd() *cobra.Command { var fChatID string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "war-room-detail", + Use: "war-room-detail ", Short: "Get war room detail", Long: `Get war room detail. @@ -2585,16 +2702,21 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - chat_name (string) (required) — Chat/group display name. - share_link (string) (required) — Join link for the war room, if provided by the IM. `, + Args: requireExactArg("chat_id"), Example: ` flashduty incident war-room-detail --data '{"chat_id":"oc_a0553eda9014c2de1b3a8f75b4e0c000","integration_id":2490562293131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "chat_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("chat-id") { body["chat_id"] = fChatID } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -2613,7 +2735,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le } cmd.Flags().StringVar(&fChatID, "chat-id", "", "Chat/group ID on the IM side. (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "IM integration ID that hosts the war room. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -2622,7 +2744,7 @@ func genIncidentsWarRoomListCmd() *cobra.Command { var fIncidentID string var fIntegrationID int64 cmd := &cobra.Command{ - Use: "war-room-list", + Use: "war-room-list ", Short: "List war rooms", Long: `List war rooms. @@ -2645,16 +2767,21 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - plugin_type (string) (required) — IM plugin type (e.g. 'feishu', 'dingtalk', 'wecom', 'slack'). - status (string) (required) — War room status. `, + Args: requireExactArg("incident_id"), Example: ` flashduty incident war-room-list --data '{"incident_id":"69da451ef77b1b51f40e83ee"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "incident_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("incident-id") { body["incident_id"] = fIncidentID } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -2673,7 +2800,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID (MongoDB ObjectID). (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Optional filter: only return war rooms for this IM integration.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_integrations.go b/internal/cli/zz_generated_integrations.go index 2214d38..fc2082b 100644 --- a/internal/cli/zz_generated_integrations.go +++ b/internal/cli/zz_generated_integrations.go @@ -49,13 +49,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty webhook history-detail --data '{"event_id":"20260412Xatt9hrXsgmFkBR78WF655","integration_id":6113996590131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("event-id") { body["event_id"] = fEventID } if cmd.Flags().Changed("integration-id") { body["integration_id"] = fIntegrationID } + return nil }) if err != nil { return err @@ -74,7 +75,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le } cmd.Flags().StringVar(&fEventID, "event-id", "", "Event ID returned by 'ListWebhookHistory'. (required)") cmd.Flags().Int64Var(&fIntegrationID, "integration-id", 0, "Integration ID the event belongs to. (required) (min 1)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -136,7 +137,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty webhook history-list --data '{"end_time":1775203200000,"integration_id":6113996590131,"limit":20,"start_time":1775116800000,"status":"success"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("asc") { body["asc"] = fAsc } @@ -167,6 +168,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("status") { body["status"] = fStatus } + return nil }) if err != nil { return err @@ -193,7 +195,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque cursor returned by a previous call for fetching the next page.") cmd.Flags().Int64Var(&fStartTime, "start-time", 0, "Window start time in Unix milliseconds. (required) (1000000000000-9999999999999)") cmd.Flags().StringVar(&fStatus, "status", "", "Filter by delivery status. [success, failed]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_issues.go b/internal/cli/zz_generated_issues.go index 7fd86ae..559cdb8 100644 --- a/internal/cli/zz_generated_issues.go +++ b/internal/cli/zz_generated_issues.go @@ -12,7 +12,7 @@ func genIssuesReadInfoCmd() *cobra.Command { var dataJSON string var fIssueID string cmd := &cobra.Command{ - Use: "issue-info", + Use: "issue-info ", Short: "Get issue detail", Long: `Get issue detail. @@ -59,13 +59,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) - versions (array) `, + Args: requireExactArg("issue_id"), Example: ` flashduty rum issue-info --data '{"issue_id":"NHEacQHi2DhXqobr9qPQz9"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "issue_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("issue-id") { body["issue_id"] = fIssueID } + return nil }) if err != nil { return err @@ -83,7 +88,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fIssueID, "issue-id", "", "Issue ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -172,7 +177,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty rum issue-list --data '{"application_ids":["eWbr4xk3ZRnLabRa6unqwD"],"end_time":1775961914595,"limit":20,"orderby":"updated_at","p":1,"start_time":1772611200000,"statuses":["for_review"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -218,6 +223,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -249,7 +255,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringSliceVar(&fStatuses, "statuses", nil, "Filter by statuses. [for_review, reviewed, ignored, resolved]") cmd.Flags().StringSliceVar(&fSuspectedCauses, "suspected-causes", nil, "Filter by suspected causes.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -259,7 +265,7 @@ func genIssuesWriteUpdateCmd() *cobra.Command { var fStatus string var fSuspectedCause string cmd := &cobra.Command{ - Use: "issue-update", + Use: "issue-update ", Short: "Update issue", Long: `Update issue. @@ -272,10 +278,14 @@ Request fields: --status string — New status. [for_review, reviewed, ignored, resolved] --suspected-cause string — Suspected cause. [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown] `, + Args: requireExactArg("issue_id"), Example: ` flashduty rum issue-update --data '{"issue_id":"NHEacQHi2DhXqobr9qPQz9","status":"resolved"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "issue_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("issue-id") { body["issue_id"] = fIssueID } @@ -285,6 +295,7 @@ Request fields: if cmd.Flags().Changed("suspected-cause") { body["suspected_cause"] = fSuspectedCause } + return nil }) if err != nil { return err @@ -308,7 +319,7 @@ Request fields: cmd.Flags().StringVar(&fIssueID, "issue-id", "", "Issue ID to update. (required)") cmd.Flags().StringVar(&fStatus, "status", "", "New status. [for_review, reviewed, ignored, resolved]") cmd.Flags().StringVar(&fSuspectedCause, "suspected-cause", "", "Suspected cause. [api.failed_request, network.error, code.exception, code.invalid_object_access, code.invalid_argument, unknown]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_mcp_servers.go b/internal/cli/zz_generated_mcp_servers.go index 5eb788a..dd765ff 100644 --- a/internal/cli/zz_generated_mcp_servers.go +++ b/internal/cli/zz_generated_mcp_servers.go @@ -12,7 +12,7 @@ func genMcpServersReadServerGetCmd() *cobra.Command { var dataJSON string var fServerID string cmd := &cobra.Command{ - Use: "mcp-server-get", + Use: "mcp-server-get ", Short: "Get MCP server detail", Long: `Get MCP server detail. @@ -54,13 +54,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. - url (string) — Endpoint URL for sse or streamable-http transport. `, + Args: requireExactArg("server_id"), Example: ` flashduty safari mcp-server-get --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "server_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("server-id") { body["server_id"] = fServerID } + return nil }) if err != nil { return err @@ -78,7 +83,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to fetch. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -141,7 +146,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty safari mcp-server-list --data '{"include_account":true,"limit":20,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -157,6 +162,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -178,7 +184,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") cmd.Flags().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped rows alongside team-scoped ones; defaults to true.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Restrict results to resources owned by these teams; intersected with the caller's visible set.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -257,7 +263,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty safari mcp-server-create --data '{"args":["-y","@modelcontextprotocol/server-github"],"command":"npx","description":"Read issues and pull requests from GitHub.","env":{"GITHUB_TOKEN":"ghp_xxx"},"server_name":"GitHub Tools","status":"enabled","team_id":0,"transport":"stdio"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("args") { body["args"] = fArgs } @@ -297,6 +303,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("url") { body["url"] = fURL } + return nil }) if err != nil { return err @@ -326,7 +333,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team for the new server; 0 for account scope.") cmd.Flags().StringVar(&fTransport, "transport", "", "Transport used to reach the server. (required) [stdio, sse, streamable-http]") cmd.Flags().StringVar(&fURL, "url", "", "Endpoint URL for sse or streamable-http transport.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -334,7 +341,7 @@ func genMcpServersWriteServerDeleteCmd() *cobra.Command { var dataJSON string var fServerID string cmd := &cobra.Command{ - Use: "mcp-server-delete", + Use: "mcp-server-delete ", Short: "Delete MCP server", Long: `Delete MCP server. @@ -345,13 +352,18 @@ API: POST /safari/mcp/server/delete (mcp-write-server-delete) Request fields: --server-id string (required) — Identifier of the server to delete. `, + Args: requireExactArg("server_id"), Example: ` flashduty safari mcp-server-delete --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "server_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("server-id") { body["server_id"] = fServerID } + return nil }) if err != nil { return err @@ -369,7 +381,7 @@ Request fields: }, } cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the server to delete. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -377,7 +389,7 @@ func genMcpServersWriteServerDisableCmd() *cobra.Command { var dataJSON string var fServerID string cmd := &cobra.Command{ - Use: "mcp-server-disable", + Use: "mcp-server-disable ", Short: "Disable MCP server", Long: `Disable MCP server. @@ -388,13 +400,18 @@ API: POST /safari/mcp/server/disable (mcp-write-server-disable) Request fields: --server-id string (required) — Identifier of the target server. `, + Args: requireExactArg("server_id"), Example: ` flashduty safari mcp-server-disable --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "server_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("server-id") { body["server_id"] = fServerID } + return nil }) if err != nil { return err @@ -412,7 +429,7 @@ Request fields: }, } cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the target server. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -420,7 +437,7 @@ func genMcpServersWriteServerEnableCmd() *cobra.Command { var dataJSON string var fServerID string cmd := &cobra.Command{ - Use: "mcp-server-enable", + Use: "mcp-server-enable ", Short: "Enable MCP server", Long: `Enable MCP server. @@ -431,13 +448,18 @@ API: POST /safari/mcp/server/enable (mcp-write-server-enable) Request fields: --server-id string (required) — Identifier of the target server. `, + Args: requireExactArg("server_id"), Example: ` flashduty safari mcp-server-enable --data '{"server_id":"mcp-2b5e8d14a7c9"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "server_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("server-id") { body["server_id"] = fServerID } + return nil }) if err != nil { return err @@ -455,7 +477,7 @@ Request fields: }, } cmd.Flags().StringVar(&fServerID, "server-id", "", "Identifier of the target server. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -475,7 +497,7 @@ func genMcpServersWriteServerUpdateCmd() *cobra.Command { var fTransport string var fURL string cmd := &cobra.Command{ - Use: "mcp-server-update", + Use: "mcp-server-update ", Short: "Update MCP server", Long: `Update MCP server. @@ -531,10 +553,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. - url (string) — Endpoint URL for sse or streamable-http transport. `, + Args: requireExactArg("server_id"), Example: ` flashduty safari mcp-server-update --data '{"description":"Read issues, PRs, and commits from GitHub.","server_id":"mcp-2b5e8d14a7c9"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "server_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("args") { body["args"] = fArgs } @@ -574,6 +600,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("url") { body["url"] = fURL } + return nil }) if err != nil { return err @@ -603,7 +630,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign the server to this team; omit to leave unchanged, 0 for account scope.") cmd.Flags().StringVar(&fTransport, "transport", "", "New transport for the server. [stdio, sse, streamable-http]") cmd.Flags().StringVar(&fURL, "url", "", "New endpoint URL for remote transports.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_members.go b/internal/cli/zz_generated_members.go index 2e880e5..a9cfe92 100644 --- a/internal/cli/zz_generated_members.go +++ b/internal/cli/zz_generated_members.go @@ -38,7 +38,7 @@ Request fields: Example: ` flashduty member delete --data '{"member_id":5068740052131}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("country-code") { body["country_code"] = fCountryCode } @@ -60,6 +60,7 @@ Request fields: if cmd.Flags().Changed("ref-id") { body["ref_id"] = fRefID } + return nil }) if err != nil { return err @@ -87,7 +88,7 @@ Request fields: cmd.Flags().StringVar(&fMemberName, "member-name", "", "Member name") cmd.Flags().StringVar(&fPhone, "phone", "", "Phone number") cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -96,7 +97,7 @@ func genMembersMemberGrantRoleCmd() *cobra.Command { var fMemberID int64 var fRoleIDs []int cmd := &cobra.Command{ - Use: "role-grant", + Use: "role-grant [...]", Short: "Grant role to member", Long: `Grant role to member. @@ -108,16 +109,21 @@ Request fields: --member-id int (required) — Member ID --role-ids []int (required) — Role IDs to grant; appended to the member's current roles (duplicates are deduplicated). `, + Args: requireArgs("role_ids"), Example: ` flashduty member role-grant --data '{"member_id":5068740052131,"role_ids":[6]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("member-id") { body["member_id"] = fMemberID } if cmd.Flags().Changed("role-ids") { body["role_ids"] = fRoleIDs } + return nil }) if err != nil { return err @@ -140,7 +146,7 @@ Request fields: } cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID (required)") cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "Role IDs to grant; appended to the member's current roles (duplicates are deduplicated). (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -180,7 +186,8 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty member info --data '{}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -194,7 +201,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -230,10 +237,11 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty member invite --data '{"members":[{"email":"charlie@example.com","locale":"en-US","member_name":"Charlie","role_ids":[6],"time_zone":"Asia/Shanghai"}]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("from") { body["from"] = fFrom } + return nil }) if err != nil { return err @@ -251,7 +259,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().StringVar(&fFrom, "from", "", "Invite source context") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -308,7 +316,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty member list --data '{"limit":5,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -330,6 +338,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -353,7 +362,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field [created_at, updated_at]") cmd.Flags().StringVar(&fQuery, "query", "", "Search keyword") cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Filter by role ID") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -368,7 +377,7 @@ func genMembersMemberResetInfoCmd() *cobra.Command { var fPhone string var fTimeZone string cmd := &cobra.Command{ - Use: "info-reset", + Use: "info-reset ", Short: "Reset member info", Long: `Reset member info. @@ -386,10 +395,14 @@ Request fields: --phone string — Phone number --time-zone string — Time zone `, + Args: requireExactArg("member_id"), Example: ` flashduty member info-reset --data '{"locale":"zh-CN","member_id":2476444212131,"member_name":"Alice","time_zone":"Asia/Shanghai"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "member_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("avatar") { body["avatar"] = fAvatar } @@ -414,6 +427,7 @@ Request fields: if cmd.Flags().Changed("time-zone") { body["time_zone"] = fTimeZone } + return nil }) if err != nil { return err @@ -442,7 +456,7 @@ Request fields: cmd.Flags().StringVar(&fMemberName, "member-name", "", "Display name (2-39 chars)") cmd.Flags().StringVar(&fPhone, "phone", "", "Phone number") cmd.Flags().StringVar(&fTimeZone, "time-zone", "", "Time zone") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -451,7 +465,7 @@ func genMembersMemberRevokeRoleCmd() *cobra.Command { var fMemberID int64 var fRoleIDs []int cmd := &cobra.Command{ - Use: "role-revoke", + Use: "role-revoke [...]", Short: "Revoke role from member", Long: `Revoke role from member. @@ -463,16 +477,21 @@ Request fields: --member-id int (required) — Member ID --role-ids []int (required) — Role IDs to remove from the member. `, + Args: requireArgs("role_ids"), Example: ` flashduty member role-revoke --data '{"member_id":5068740052131,"role_ids":[6]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("member-id") { body["member_id"] = fMemberID } if cmd.Flags().Changed("role-ids") { body["role_ids"] = fRoleIDs } + return nil }) if err != nil { return err @@ -495,7 +514,7 @@ Request fields: } cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID (required)") cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "Role IDs to remove from the member. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -504,7 +523,7 @@ func genMembersMemberUpdateRoleCmd() *cobra.Command { var fMemberID int64 var fRoleIDs []int cmd := &cobra.Command{ - Use: "role-update", + Use: "role-update [...]", Short: "Update member roles", Long: `Update member roles. @@ -516,16 +535,21 @@ Request fields: --member-id int (required) — Member ID --role-ids []int (required) — New set of role IDs `, + Args: requireArgs("role_ids"), Example: ` flashduty member role-update --data '{"member_id":5068740052131,"role_ids":[2,6]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("member-id") { body["member_id"] = fMemberID } if cmd.Flags().Changed("role-ids") { body["role_ids"] = fRoleIDs } + return nil }) if err != nil { return err @@ -548,7 +572,7 @@ Request fields: } cmd.Flags().Int64Var(&fMemberID, "member-id", 0, "Member ID (required)") cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "New set of role IDs (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -556,7 +580,7 @@ func genMembersPersonInfosCmd() *cobra.Command { var dataJSON string var fPersonIDs []int cmd := &cobra.Command{ - Use: "infos", + Use: "infos [...]", Short: "Batch get persons", Long: `Batch get persons. @@ -582,13 +606,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - status (string) — Person status. 'enabled' — active; 'pending' — invited but not yet accepted; 'deleted' — removed. [enabled, pending, deleted] - time_zone (string) — Time zone `, + Args: requireArgs("person_ids"), Example: ` flashduty person infos --data '{"person_ids":[2476444212131,3790925372131]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "person_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("person-ids") { body["person_ids"] = fPersonIDs } + return nil }) if err != nil { return err @@ -606,7 +635,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fPersonIDs, "person-ids", nil, "List of person IDs (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_notification_templates.go b/internal/cli/zz_generated_notification_templates.go index 4432956..17a9f27 100644 --- a/internal/cli/zz_generated_notification_templates.go +++ b/internal/cli/zz_generated_notification_templates.go @@ -12,7 +12,7 @@ func genNotificationTemplatesReadInfoCmd() *cobra.Command { var dataJSON string var fTemplateID string cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get template detail", Long: `Get template detail. @@ -50,13 +50,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - wecom_app (string) (required) — WeCom app message template source. - zoom (string) (required) — Zoom bot message template source. `, + Args: requireExactArg("template_id"), Example: ` flashduty template info --data '{"template_id":"6605a1b2c3d4e5f6a7b8c9d0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "template_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("template-id") { body["template_id"] = fTemplateID } + return nil }) if err != nil { return err @@ -74,7 +79,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Target template ID. Pass '000000000000000000000001' to address the built-in preset. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -142,7 +147,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty template list --data '{"asc":false,"is_my_team":false,"limit":20,"orderby":"updated_at","p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -170,6 +175,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -195,7 +201,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") cmd.Flags().StringVar(&fQuery, "query", "", "Regex or substring match on template_name.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by specific team IDs.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -226,7 +232,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty template preview --data '{"content":"Incident {{.Title}} is {{.Status}}","incident_id":"664a1b2c3d4e5f6a7b8c9d0e","type":"feishu_app"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("content") { body["content"] = fContent } @@ -236,6 +242,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("type") { body["type"] = fType } + return nil }) if err != nil { return err @@ -255,7 +262,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fContent, "content", "", "Template content to render. (required)") cmd.Flags().StringVar(&fIncidentID, "incident-id", "", "Incident ID whose data is used to render the template; mock data is used when omitted. A MongoDB ObjectID hex string.") cmd.Flags().StringVar(&fType, "type", "", "Template channel type that selects the rendering engine. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -313,7 +320,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty template create --data '{"description":"Default template for production incidents.","email":"Incident {{ .IncidentName }} on {{ .Severity }}","sms":"[Flashduty] {{ .IncidentName }} — {{ .Severity }}","team_id":0,"template_name":"Prod incident default"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -365,6 +372,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("zoom") { body["zoom"] = fZoom } + return nil }) if err != nil { return err @@ -398,7 +406,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fWecom, "wecom", "", "WeCom robot message template source.") cmd.Flags().StringVar(&fWecomApp, "wecom-app", "", "WeCom app message template source.") cmd.Flags().StringVar(&fZoom, "zoom", "", "Zoom bot message template source.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -406,7 +414,7 @@ func genNotificationTemplatesWriteDeleteCmd() *cobra.Command { var dataJSON string var fTemplateID string cmd := &cobra.Command{ - Use: "delete", + Use: "delete ", Short: "Delete a template", Long: `Delete a template. @@ -417,13 +425,18 @@ API: POST /template/delete (template-write-delete) Request fields: --template-id string (required) — Target template ID. Pass '000000000000000000000001' to address the built-in preset. `, + Args: requireExactArg("template_id"), Example: ` flashduty template delete --data '{"template_id":"6605a1b2c3d4e5f6a7b8c9d0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "template_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("template-id") { body["template_id"] = fTemplateID } + return nil }) if err != nil { return err @@ -445,7 +458,7 @@ Request fields: }, } cmd.Flags().StringVar(&fTemplateID, "template-id", "", "Target template ID. Pass '000000000000000000000001' to address the built-in preset. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -470,7 +483,7 @@ func genNotificationTemplatesWriteUpdateCmd() *cobra.Command { var fWecomApp string var fZoom string cmd := &cobra.Command{ - Use: "update", + Use: "update ", Short: "Update a template", Long: `Update a template. @@ -498,10 +511,14 @@ Request fields: --wecom-app string — WeCom app message template source. --zoom string — Zoom bot message template source. `, + Args: requireExactArg("template_id"), Example: ` flashduty template update --data '{"description":"Updated description.","email":"Incident {{ .IncidentName }} on {{ .Severity }}","sms":"[Flashduty] {{ .IncidentName }} — {{ .Severity }}","template_id":"6605a1b2c3d4e5f6a7b8c9d0","template_name":"Prod incident default"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "template_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -556,6 +573,7 @@ Request fields: if cmd.Flags().Changed("zoom") { body["zoom"] = fZoom } + return nil }) if err != nil { return err @@ -594,7 +612,7 @@ Request fields: cmd.Flags().StringVar(&fWecom, "wecom", "", "WeCom robot message template source.") cmd.Flags().StringVar(&fWecomApp, "wecom-app", "", "WeCom app message template source.") cmd.Flags().StringVar(&fZoom, "zoom", "", "Zoom bot message template source.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_roles_permissions.go b/internal/cli/zz_generated_roles_permissions.go index 1c2d888..d4bbc98 100644 --- a/internal/cli/zz_generated_roles_permissions.go +++ b/internal/cli/zz_generated_roles_permissions.go @@ -12,7 +12,7 @@ func genRolesPermissionsReadInfoCmd() *cobra.Command { var dataJSON string var fRoleID int64 cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get role detail", Long: `Get role detail. @@ -33,13 +33,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - status (string) (required) — Role status. [enabled, disabled] - updated_at (integer) (required) — Unix epoch seconds the role was last updated. `, + Args: requireExactArg("role_id"), Example: ` flashduty role info --data '{"role_id":2}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -57,7 +62,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -93,13 +98,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty role list --data '{"asc":false,"orderby":"created_at"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("asc") { body["asc"] = fAsc } if cmd.Flags().Changed("orderby") { body["orderby"] = fOrderby } + return nil }) if err != nil { return err @@ -118,7 +124,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().BoolVar(&fAsc, "asc", false, "Ascending sort order.") cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -153,13 +159,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty role permission-list --data '{"role_ids":[150],"with_all":true}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("role-ids") { body["role_ids"] = fRoleIDs } if cmd.Flags().Changed("with-all") { body["with_all"] = fWithAll } + return nil }) if err != nil { return err @@ -178,7 +185,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().IntSliceVar(&fRoleIDs, "role-ids", nil, "Filter to permissions granted to these roles.") cmd.Flags().BoolVar(&fWithAll, "with-all", false, "If true, return all permissions with is_granted set to indicate which are granted.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -204,10 +211,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty role permission-factor-list --data '{"factor_types":["api"]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("factor-types") { body["factor_types"] = fFactorTypes } + return nil }) if err != nil { return err @@ -225,7 +233,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().StringSliceVar(&fFactorTypes, "factor-types", nil, "Filter by factor type. [api, button, visit, menu, url]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -233,7 +241,7 @@ func genRolesPermissionsWriteDeleteCmd() *cobra.Command { var dataJSON string var fRoleID int64 cmd := &cobra.Command{ - Use: "delete", + Use: "delete ", Short: "Delete a role", Long: `Delete a role. @@ -244,13 +252,18 @@ API: POST /role/delete (role-write-delete) Request fields: --role-id int (required) — Role ID. `, + Args: requireExactArg("role_id"), Example: ` flashduty role delete --data '{"role_id":150}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -272,7 +285,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -280,7 +293,7 @@ func genRolesPermissionsWriteDisableCmd() *cobra.Command { var dataJSON string var fRoleID int64 cmd := &cobra.Command{ - Use: "disable", + Use: "disable ", Short: "Disable a role", Long: `Disable a role. @@ -291,13 +304,18 @@ API: POST /role/disable (role-write-disable) Request fields: --role-id int (required) — Role ID. `, + Args: requireExactArg("role_id"), Example: ` flashduty role disable --data '{"role_id":150}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -319,7 +337,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -327,7 +345,7 @@ func genRolesPermissionsWriteEnableCmd() *cobra.Command { var dataJSON string var fRoleID int64 cmd := &cobra.Command{ - Use: "enable", + Use: "enable ", Short: "Enable a role", Long: `Enable a role. @@ -338,13 +356,18 @@ API: POST /role/enable (role-write-enable) Request fields: --role-id int (required) — Role ID. `, + Args: requireExactArg("role_id"), Example: ` flashduty role enable --data '{"role_id":150}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "role_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -366,7 +389,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -375,7 +398,7 @@ func genRolesPermissionsWriteGrantRoleCmd() *cobra.Command { var fMemberIDs []int var fRoleID int64 cmd := &cobra.Command{ - Use: "member-grant", + Use: "member-grant [...]", Short: "Grant role to members", Long: `Grant role to members. @@ -387,16 +410,21 @@ Request fields: --member-ids []int (required) — Member IDs to grant/revoke the role. Max 100. --role-id int (required) — Role ID to grant or revoke. `, + Args: requireArgs("member_ids"), Example: ` flashduty role member-grant --data '{"member_ids":[80011,80012],"role_id":150}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "member_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("member-ids") { body["member_ids"] = fMemberIDs } if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -419,7 +447,7 @@ Request fields: } cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Member IDs to grant/revoke the role. Max 100. (required)") cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID to grant or revoke. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -428,7 +456,7 @@ func genRolesPermissionsWriteRevokeRoleCmd() *cobra.Command { var fMemberIDs []int var fRoleID int64 cmd := &cobra.Command{ - Use: "member-revoke", + Use: "member-revoke [...]", Short: "Revoke role from members", Long: `Revoke role from members. @@ -440,16 +468,21 @@ Request fields: --member-ids []int (required) — Member IDs to grant/revoke the role. Max 100. --role-id int (required) — Role ID to grant or revoke. `, + Args: requireArgs("member_ids"), Example: ` flashduty role member-revoke --data '{"member_ids":[80011],"role_id":150}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "member_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("member-ids") { body["member_ids"] = fMemberIDs } if cmd.Flags().Changed("role-id") { body["role_id"] = fRoleID } + return nil }) if err != nil { return err @@ -472,7 +505,7 @@ Request fields: } cmd.Flags().IntSliceVar(&fMemberIDs, "member-ids", nil, "Member IDs to grant/revoke the role. Max 100. (required)") cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID to grant or revoke. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -504,7 +537,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty role upsert --data '{"description":"Manage on-call rotations and incidents.","permission_ids":[501,502],"role_name":"On-call Manager"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -517,6 +550,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("role-name") { body["role_name"] = fRoleName } + return nil }) if err != nil { return err @@ -537,7 +571,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().IntSliceVar(&fPermissionIDs, "permission-ids", nil, "Permission IDs to grant. Replaces the existing set.") cmd.Flags().Int64Var(&fRoleID, "role-id", 0, "Role ID. Omit or set to 0 to create.") cmd.Flags().StringVar(&fRoleName, "role-name", "", "Role display name. 1–39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_rule_sets.go b/internal/cli/zz_generated_rule_sets.go index bbfdc89..ccb14cf 100644 --- a/internal/cli/zz_generated_rule_sets.go +++ b/internal/cli/zz_generated_rule_sets.go @@ -44,7 +44,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit store-ruleset-create --data '{"note":"CPU usage alerts","open_flag":1,"payload":"[{\"prom_ql\":\"rate(cpu_usage[5m]) \u003e 0.8\"}]","type_ident":"prometheus"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("note") { body["note"] = fNote } @@ -57,6 +57,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("type-ident") { body["type_ident"] = fTypeIdent } + return nil }) if err != nil { return err @@ -77,7 +78,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fOpenFlag, "open-flag", 0, "Sharing scope. '0' = private (creator only), '1' = account-shared, '2' = public. Defaults to '0' if omitted.") cmd.Flags().StringVar(&fPayload, "payload", "", "JSON string containing the alert rule definitions. (required)") cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier this ruleset applies to, e.g. 'prometheus'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -99,10 +100,11 @@ Request fields: Example: ` flashduty monit store-ruleset-delete --data '{"id":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -124,7 +126,7 @@ Request fields: }, } cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -158,10 +160,11 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit store-ruleset-info --data '{"id":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } + return nil }) if err != nil { return err @@ -179,7 +182,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().Int64Var(&fID, "id", 0, "Resource ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -213,10 +216,11 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' Example: ` flashduty monit store-ruleset-list --data '{"type_ident":"prometheus"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("type-ident") { body["type_ident"] = fTypeIdent } + return nil }) if err != nil { return err @@ -234,7 +238,7 @@ Response fields ('data' is a TOP-LEVEL array of these row objects — pipe 'jq ' }, } cmd.Flags().StringVar(&fTypeIdent, "type-ident", "", "Datasource type identifier to filter by, e.g. 'prometheus'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -274,7 +278,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty monit store-ruleset-update --data '{"id":1,"note":"Updated CPU alerts","open_flag":2,"payload":"[{\"prom_ql\":\"rate(cpu_usage[5m]) \u003e 0.9\"}]"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("id") { body["id"] = fID } @@ -287,6 +291,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("payload") { body["payload"] = fPayload } + return nil }) if err != nil { return err @@ -307,7 +312,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fNote, "note", "", "New description. (required)") cmd.Flags().Int64Var(&fOpenFlag, "open-flag", 0, "New sharing scope. '0' = private, '1' = account-shared, '2' = public.") cmd.Flags().StringVar(&fPayload, "payload", "", "New JSON string of alert rule definitions. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_schedules.go b/internal/cli/zz_generated_schedules.go index 13e4861..2547d36 100644 --- a/internal/cli/zz_generated_schedules.go +++ b/internal/cli/zz_generated_schedules.go @@ -105,7 +105,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -127,6 +127,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -150,7 +151,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fScheduleName, "schedule-name", "", "Schedule display name. Max 40 characters. (≤40 chars)") cmd.Flags().StringVar(&fStart, "start", "", "Preview window start (Unix seconds, 10 digits). Required for /schedule/preview. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -158,7 +159,7 @@ func genSchedulesDeleteCmd() *cobra.Command { var dataJSON string var fScheduleIDs []int cmd := &cobra.Command{ - Use: "delete", + Use: "delete [...]", Short: "Delete schedules", Long: `Delete schedules. @@ -169,13 +170,18 @@ API: POST /schedule/delete (scheduleDelete) Request fields: --schedule-ids []int (required) — Schedule IDs to operate on. `, + Args: requireArgs("schedule_ids"), Example: ` flashduty schedule delete --data '{"schedule_ids":[2001]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schedule_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("schedule-ids") { body["schedule_ids"] = fScheduleIDs } + return nil }) if err != nil { return err @@ -197,7 +203,7 @@ Request fields: }, } cmd.Flags().IntSliceVar(&fScheduleIDs, "schedule-ids", nil, "Schedule IDs to operate on. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -207,7 +213,7 @@ func genSchedulesInfoCmd() *cobra.Command { var fScheduleID int64 var fStart string cmd := &cobra.Command{ - Use: "info", + Use: "info ", Short: "Get schedule info", Long: `Get schedule info. @@ -365,6 +371,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - update_at (integer) (required) — Last update timestamp (Unix seconds). - update_by (integer) (required) — Last updater person ID. `, + Args: requireExactArg("schedule_id"), Example: ` flashduty schedule info --data '{"end":1712086400,"schedule_id":2001,"start":1712000000}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -376,7 +383,10 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schedule_id", "int"); err != nil { + return err + } if okEnd { body["end"] = vEnd } @@ -386,6 +396,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if okStart { body["start"] = vStart } + return nil }) if err != nil { return err @@ -405,7 +416,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fEnd, "end", "", "Preview end timestamp (Unix seconds, 10 digits). (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().Int64Var(&fScheduleID, "schedule-id", 0, "Schedule ID. (required)") cmd.Flags().StringVar(&fStart, "start", "", "Preview start timestamp (Unix seconds, 10 digits). (required) Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -413,7 +424,7 @@ func genSchedulesInfosCmd() *cobra.Command { var dataJSON string var fScheduleIDs []int cmd := &cobra.Command{ - Use: "infos", + Use: "infos [...]", Short: "Batch get schedules", Long: `Batch get schedules. @@ -543,13 +554,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - update_at (integer) (required) — Last update timestamp (Unix seconds). - update_by (integer) (required) — Last updater person ID. `, + Args: requireArgs("schedule_ids"), Example: ` flashduty schedule infos --data '{"schedule_ids":[2001,2002,2003]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "schedule_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("schedule-ids") { body["schedule_ids"] = fScheduleIDs } + return nil }) if err != nil { return err @@ -567,7 +583,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fScheduleIDs, "schedule-ids", nil, "Schedule ID list. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -733,7 +749,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -761,6 +777,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -786,7 +803,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fQuery, "query", "", "Search keyword matched against schedule names.") cmd.Flags().StringVar(&fStart, "start", "", "When set together with end, computed layer schedules are returned. Span must be less than 45 days. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Filter by team IDs.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1029,7 +1046,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -1051,6 +1068,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -1074,7 +1092,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fScheduleName, "schedule-name", "", "Schedule display name. Max 40 characters. (≤40 chars)") cmd.Flags().StringVar(&fStart, "start", "", "Preview window start (Unix seconds, 10 digits). Required for /schedule/preview. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1225,13 +1243,14 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if okEnd { body["end"] = vEnd } if okStart { body["start"] = vStart } + return nil }) if err != nil { return err @@ -1250,7 +1269,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().StringVar(&fEnd, "end", "", "Window end (Unix seconds, 10 digits). Must be within 30 days of start. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().StringVar(&fStart, "start", "", "Window start (Unix seconds, 10 digits). Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1348,7 +1367,7 @@ Request fields: if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -1370,6 +1389,7 @@ Request fields: if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -1397,7 +1417,7 @@ Request fields: cmd.Flags().StringVar(&fScheduleName, "schedule-name", "", "Schedule display name. Max 40 characters. (≤40 chars)") cmd.Flags().StringVar(&fStart, "start", "", "Preview window start (Unix seconds, 10 digits). Required for /schedule/preview. Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Owning team ID.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_sessions.go b/internal/cli/zz_generated_sessions.go index b9befdd..2702009 100644 --- a/internal/cli/zz_generated_sessions.go +++ b/internal/cli/zz_generated_sessions.go @@ -15,7 +15,7 @@ func genSessionsInfoCmd() *cobra.Command { var fSearchAfterCtx string var fSessionID string cmd := &cobra.Command{ - Use: "session-get", + Use: "session-get ", Short: "Get session detail", Long: `Get session detail. @@ -88,10 +88,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - reasoning_tokens (integer) — Total reasoning/thinking tokens. - updated_at (integer) — Unix timestamp in milliseconds of the last session update. `, + Args: requireExactArg("session_id"), Example: ` flashduty safari session-get --data '{"num_recent_events":50,"session_id":"sess_f8oDvqiG64uur6sBNsTc4u"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "session_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("limit") { body["limit"] = fLimit } @@ -104,6 +108,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("session-id") { body["session_id"] = fSessionID } + return nil }) if err != nil { return err @@ -124,7 +129,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().Int64Var(&fNumRecentEvents, "num-recent-events", 0, "Number of most-recent events to return; 0 uses the server default. (0-1000)") cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Opaque keyset cursor from a previous response's search_after_ctx, to page backward through older events. (≤4096 chars)") cmd.Flags().StringVar(&fSessionID, "session-id", "", "Session identifier. (required) (≥1 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -211,7 +216,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty safari session-list --data '{"app_name":"ai-sre","limit":2,"orderby":"updated_at","scope":"all"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -248,6 +253,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -276,7 +282,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fScope, "scope", "", "Visibility scope: all (own + member-of-team rows, the default), personal (own only), or team (member teams only). [all, personal, team]") cmd.Flags().StringVar(&fStatus, "status", "", "Archive bucket: active (default, not archived), archived, or all. [active, archived, all]") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Optional explicit team filter; intersected with the caller's visible set / scope.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_skills.go b/internal/cli/zz_generated_skills.go index 4a4967a..5b4f642 100644 --- a/internal/cli/zz_generated_skills.go +++ b/internal/cli/zz_generated_skills.go @@ -12,7 +12,7 @@ func genSkillsReadDownloadCmd() *cobra.Command { var dataJSON string var fSkillID string cmd := &cobra.Command{ - Use: "skill-download", + Use: "skill-download ", Short: "Download skill", Long: `Download skill. @@ -23,13 +23,18 @@ API: POST /safari/skill/download (skill-read-download) Request fields: --skill-id string (required) — Identifier of the skill to download. `, + Args: requireExactArg("skill_id"), Example: ` flashduty safari skill-download --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("skill-id") { body["skill_id"] = fSkillID } + return nil }) if err != nil { return err @@ -47,7 +52,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to download. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -55,7 +60,7 @@ func genSkillsReadEnableCmd() *cobra.Command { var dataJSON string var fSkillID string cmd := &cobra.Command{ - Use: "skill-enable", + Use: "skill-enable ", Short: "Enable skill", Long: `Enable skill. @@ -66,13 +71,18 @@ API: POST /safari/skill/enable (skill-read-enable) Request fields: --skill-id string (required) — Identifier of the target skill. `, + Args: requireExactArg("skill_id"), Example: ` flashduty safari skill-enable --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("skill-id") { body["skill_id"] = fSkillID } + return nil }) if err != nil { return err @@ -90,7 +100,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the target skill. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -98,7 +108,7 @@ func genSkillsReadGetCmd() *cobra.Command { var dataJSON string var fSkillID string cmd := &cobra.Command{ - Use: "skill-get", + Use: "skill-get ", Short: "Get skill detail", Long: `Get skill detail. @@ -134,13 +144,18 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. - version (string) — Skill version string from its frontmatter. `, + Args: requireExactArg("skill_id"), Example: ` flashduty safari skill-get --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("skill-id") { body["skill_id"] = fSkillID } + return nil }) if err != nil { return err @@ -158,7 +173,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to fetch. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -215,7 +230,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty safari skill-list --data '{"include_account":true,"limit":20,"p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -231,6 +246,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -252,7 +268,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fSearchAfterCtx, "search-after-ctx", "", "Request field ") cmd.Flags().BoolVar(&fIncludeAccount, "include-account", false, "Include account-scoped rows alongside team-scoped ones; defaults to true.") cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "Restrict results to resources owned by these teams; intersected with the caller's visible set.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -260,7 +276,7 @@ func genSkillsWriteDeleteCmd() *cobra.Command { var dataJSON string var fSkillID string cmd := &cobra.Command{ - Use: "skill-delete", + Use: "skill-delete ", Short: "Delete skill", Long: `Delete skill. @@ -271,13 +287,18 @@ API: POST /safari/skill/delete (skill-write-delete) Request fields: --skill-id string (required) — Identifier of the skill to delete. `, + Args: requireExactArg("skill_id"), Example: ` flashduty safari skill-delete --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("skill-id") { body["skill_id"] = fSkillID } + return nil }) if err != nil { return err @@ -295,7 +316,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to delete. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -303,7 +324,7 @@ func genSkillsWriteDisableCmd() *cobra.Command { var dataJSON string var fSkillID string cmd := &cobra.Command{ - Use: "skill-disable", + Use: "skill-disable ", Short: "Disable skill", Long: `Disable skill. @@ -314,13 +335,18 @@ API: POST /safari/skill/disable (skill-write-disable) Request fields: --skill-id string (required) — Identifier of the target skill. `, + Args: requireExactArg("skill_id"), Example: ` flashduty safari skill-disable --data '{"skill_id":"skl-7f3a9c21b8e0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("skill-id") { body["skill_id"] = fSkillID } + return nil }) if err != nil { return err @@ -338,7 +364,7 @@ Request fields: }, } cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the target skill. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -348,7 +374,7 @@ func genSkillsWriteUpdateCmd() *cobra.Command { var fSkillID string var fTeamID int64 cmd := &cobra.Command{ - Use: "skill-update", + Use: "skill-update ", Short: "Update skill", Long: `Update skill. @@ -386,10 +412,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - updated_at (integer) (required) — Last-update time as a Unix timestamp in milliseconds. - version (string) — Skill version string from its frontmatter. `, + Args: requireExactArg("skill_id"), Example: ` flashduty safari skill-update --data '{"description":"Diagnose unhealthy Kubernetes workloads.","skill_id":"skl-7f3a9c21b8e0"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "skill_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("description") { body["description"] = fDescription } @@ -399,6 +429,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-id") { body["team_id"] = fTeamID } + return nil }) if err != nil { return err @@ -418,7 +449,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fDescription, "description", "", "New description for the skill. (≤1024 chars)") cmd.Flags().StringVar(&fSkillID, "skill-id", "", "Identifier of the skill to update. (required)") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Reassign the skill to this team; omit to leave unchanged, 0 for account scope.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -460,7 +491,8 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le `, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -474,7 +506,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_sourcemaps.go b/internal/cli/zz_generated_sourcemaps.go index ac52974..3f70f77 100644 --- a/internal/cli/zz_generated_sourcemaps.go +++ b/internal/cli/zz_generated_sourcemaps.go @@ -64,7 +64,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty sourcemap list --data '{"end_time":1712700000000,"limit":20,"p":1,"services":["my-web-app"],"start_time":1712000000000,"type":"browser"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -104,6 +104,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("versions") { body["versions"] = fVersions } + return nil }) if err != nil { return err @@ -133,7 +134,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fType, "type", "", "Platform type. Defaults to 'browser' when omitted. [browser, android, ios]") cmd.Flags().StringVar(&fUuid, "uuid", "", "iOS only. Filter by dSYM bundle UUID. Max 200 characters.") cmd.Flags().StringSliceVar(&fVersions, "versions", nil, "Filter by version strings. Up to 100 values.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_status_pages.go b/internal/cli/zz_generated_status_pages.go index ab9cf26..421eb53 100644 --- a/internal/cli/zz_generated_status_pages.go +++ b/internal/cli/zz_generated_status_pages.go @@ -59,7 +59,8 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; `, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + return nil }) if err != nil { return err @@ -73,7 +74,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }) }, } - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -82,7 +83,7 @@ func genStatusPagesChangeActiveListCmd() *cobra.Command { var fPageID int64 var fType string cmd := &cobra.Command{ - Use: "change-active-list", + Use: "change-active-list ", Short: "List active status page events", Long: `List active status page events. @@ -129,15 +130,20 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] - update_id (string) (required) — Update ID. `, + Args: requireExactArg("page_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "page_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } if cmd.Flags().Changed("type") { body["type"] = fType } + return nil }) if err != nil { return err @@ -156,7 +162,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; } cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") cmd.Flags().StringVar(&fType, "type", "", "Event type filter. Required. Returns only in-progress (non-terminal) events — 'investigating'/'identified'/'monitoring' for 'incident', 'scheduled'/'ongoing' for 'maintenance'. (required) [incident, maintenance]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -175,7 +181,7 @@ func genStatusPagesChangeCreateCmd() *cobra.Command { var fTitle string var fType string cmd := &cobra.Command{ - Use: "change-create", + Use: "change-create ", Short: "Create status page event", Long: `Create status page event. @@ -209,6 +215,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - change_id (integer) (required) — Newly created event ID. - change_name (string) (required) — Event title (echoed from the request). `, + Args: requireExactArg("page_id"), Example: ` flashduty status-page change-create --data '{"description":"We are investigating degraded performance affecting the web console.","notify_subscribers":true,"page_id":5750613685214,"start_at_seconds":1712000000,"status":"investigating","title":"Web Console Degraded Performance","type":"incident","updates":[{"component_changes":[{"component_id":"01KC3GAZ6ZJE40H55GM31RPWZE","status":"degraded"}],"description":"We are currently investigating an issue affecting some users.","status":"investigating"}]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { @@ -220,7 +227,10 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "page_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("auto-update-by-schedule") { body["auto_update_by_schedule"] = fAutoUpdateBySchedule } @@ -257,6 +267,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("type") { body["type"] = fType } + return nil }) if err != nil { return err @@ -285,7 +296,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fStatus, "status", "", "Initial event status. 'investigating'/'identified'/'monitoring'/'resolved' apply to incidents; 'scheduled'/'ongoing'/'completed' apply to maintenances. (required) [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]") cmd.Flags().StringVar(&fTitle, "title", "", "Event title, up to 255 characters. (required) (≤255 chars)") cmd.Flags().StringVar(&fType, "type", "", "Event type. (required) [incident, maintenance]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -309,13 +320,14 @@ Request fields: Example: ` flashduty status-page change-delete --data '{"change_id":5821693893131,"page_id":5750613685214}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("change-id") { body["change_id"] = fChangeID } if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } + return nil }) if err != nil { return err @@ -338,7 +350,7 @@ Request fields: } cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Target event ID. (required)") cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -395,13 +407,14 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le `, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } if cmd.Flags().Changed("change-id") { body["change_id"] = fChangeID } + return nil }) if err != nil { return err @@ -420,7 +433,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le } cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Event (change) ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -432,7 +445,7 @@ func genStatusPagesChangeListCmd() *cobra.Command { var fType string var fStatus string cmd := &cobra.Command{ - Use: "change-list", + Use: "change-list ", Short: "List status page events", Long: `List status page events. @@ -482,6 +495,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - status (string) — Event status after this update. Omitted when the update does not change the overall status. [investigating, identified, monitoring, resolved, scheduled, ongoing, completed] - update_id (string) (required) — Update ID. `, + Args: requireExactArg("page_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { vStartAtSeconds, okStartAtSeconds, err := genParseTimeFlag(cmd, "start-at-seconds", fStartAtSeconds) @@ -492,7 +506,10 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "page_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } @@ -508,6 +525,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("status") { body["status"] = fStatus } + return nil }) if err != nil { return err @@ -529,7 +547,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fEndAtSeconds, "end-at-seconds", "", "Filter events started at or before this unix timestamp (seconds). Accepts a duration (7d, 24h), '+7d' for the future, 'now', a date, or Unix seconds.") cmd.Flags().StringVar(&fType, "type", "", "Event type filter. Required. (required) [incident, maintenance]") cmd.Flags().StringVar(&fStatus, "status", "", "Event status filter. Required. Must be a status valid for the given 'type' (e.g. 'investigating'/'identified'/'monitoring'/'resolved' for incidents; 'scheduled'/'ongoing'/'completed' for maintenances). (required) [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -569,7 +587,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if okAtSeconds { body["at_seconds"] = vAtSeconds } @@ -585,6 +603,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("status") { body["status"] = fStatus } + return nil }) if err != nil { return err @@ -606,7 +625,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fDescription, "description", "", "Update description (Markdown). Required.") cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") cmd.Flags().StringVar(&fStatus, "status", "", "New event status. Must match the event type. When the status transitions to 'resolved' or 'completed', all referenced components must become 'operational'. (required) [investigating, identified, monitoring, resolved, scheduled, ongoing, completed]") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -632,7 +651,7 @@ Request fields: Example: ` flashduty status-page change-timeline-delete --data '{"change_id":5821693893131,"page_id":5750613685214,"update_id":"01KP0311872NVYFRRQ82FWXAP4"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("change-id") { body["change_id"] = fChangeID } @@ -642,6 +661,7 @@ Request fields: if cmd.Flags().Changed("update-id") { body["update_id"] = fUpdateID } + return nil }) if err != nil { return err @@ -665,7 +685,7 @@ Request fields: cmd.Flags().Int64Var(&fChangeID, "change-id", 0, "Parent event ID. (required)") cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") cmd.Flags().StringVar(&fUpdateID, "update-id", "", "Timeline update ID to delete. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -699,7 +719,7 @@ Request fields: if err != nil { return err } - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if okAtSeconds { body["at_seconds"] = vAtSeconds } @@ -715,6 +735,7 @@ Request fields: if cmd.Flags().Changed("update-id") { body["update_id"] = fUpdateID } + return nil }) if err != nil { return err @@ -740,7 +761,7 @@ Request fields: cmd.Flags().StringVar(&fDescription, "description", "", "New update description (Markdown).") cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") cmd.Flags().StringVar(&fUpdateID, "update-id", "", "Target timeline update ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -770,7 +791,7 @@ Request fields: Example: ` flashduty status-page change-update --data '{"change_id":5821693893131,"page_id":5750613685214,"title":"Web Console Degraded Performance (Updated)"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("change-id") { body["change_id"] = fChangeID } @@ -786,6 +807,7 @@ Request fields: if cmd.Flags().Changed("title") { body["title"] = fTitle } + return nil }) if err != nil { return err @@ -811,7 +833,7 @@ Request fields: cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") cmd.Flags().IntSliceVar(&fResponders, "responders", nil, "Member IDs responsible for this event. Pass the full replacement list.") cmd.Flags().StringVar(&fTitle, "title", "", "New event title, up to 255 characters. Omit to keep the existing value. (≤255 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -840,7 +862,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty status-page migrate-email-subscribers --data '{"api_key":"sk-stsp-xxxxxxxxxxxxxxxxxxxx","source_page_id":"abcdefghij","target_page_id":5750613685214}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("api-key") { body["api_key"] = fAPIKey } @@ -850,6 +872,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("target-page-id") { body["target_page_id"] = fTargetPageID } + return nil }) if err != nil { return err @@ -869,7 +892,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fAPIKey, "api-key", "", "Atlassian Statuspage API key with access to the source page. (required)") cmd.Flags().StringVar(&fSourcePageID, "source-page-id", "", "Atlassian Statuspage source page ID. (required)") cmd.Flags().Int64Var(&fTargetPageID, "target-page-id", 0, "Flashduty target status page ID that will receive the imported subscribers. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -879,7 +902,7 @@ func genStatusPagesMigrateStructureCmd() *cobra.Command { var fSourcePageID string var fURLName string cmd := &cobra.Command{ - Use: "migrate-structure", + Use: "migrate-structure ", Short: "Migrate status page structure", Long: `Migrate status page structure. @@ -895,10 +918,14 @@ Request fields: Response fields ('data' envelope is unwrapped — these fields are at the top level): - job_id (string) (required) — Migration job ID. Use this to poll status or request cancellation. `, + Args: requireExactArg("source_page_id"), Example: ` flashduty status-page migrate-structure --data '{"api_key":"sk-stsp-xxxxxxxxxxxxxxxxxxxx","source_page_id":"abcdefghij"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "source_page_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("api-key") { body["api_key"] = fAPIKey } @@ -908,6 +935,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("url-name") { body["url_name"] = fURLName } + return nil }) if err != nil { return err @@ -927,7 +955,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fAPIKey, "api-key", "", "Atlassian Statuspage API key with access to the source page. (required)") cmd.Flags().StringVar(&fSourcePageID, "source-page-id", "", "Atlassian Statuspage source page ID. (required)") cmd.Flags().StringVar(&fURLName, "url-name", "", "Target URL name for the migrated status page. When omitted, the source page's URL name is reused.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -935,7 +963,7 @@ func genStatusPagesMigrationCancelCmd() *cobra.Command { var dataJSON string var fJobID string cmd := &cobra.Command{ - Use: "migration-cancel", + Use: "migration-cancel ", Short: "Cancel status page migration", Long: `Cancel status page migration. @@ -946,13 +974,18 @@ API: POST /status-page/migration/cancel (statusPageMigrationCancel) Request fields: --job-id string (required) — Migration job ID. `, + Args: requireExactArg("job_id"), Example: ` flashduty status-page migration-cancel --data '{"job_id":"01KP0311872NVYFRRQ82FW0001"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "job_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("job-id") { body["job_id"] = fJobID } + return nil }) if err != nil { return err @@ -974,7 +1007,7 @@ Request fields: }, } cmd.Flags().StringVar(&fJobID, "job-id", "", "Migration job ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -982,7 +1015,7 @@ func genStatusPagesMigrationStatusCmd() *cobra.Command { var dataJSON string var fJobID string cmd := &cobra.Command{ - Use: "migration-status", + Use: "migration-status ", Short: "Get migration status", Long: `Get migration status. @@ -1015,12 +1048,17 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le - target_page_id (integer) (required) — Flashduty target status page ID. Set once the job produces one, or supplied up front for subscriber migration. - updated_at (integer) (required) — Last status update time, unix seconds. `, + Args: requireExactArg("job_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "job_id", "string"); err != nil { + return err + } if cmd.Flags().Changed("job-id") { body["job_id"] = fJobID } + return nil }) if err != nil { return err @@ -1038,7 +1076,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le }, } cmd.Flags().StringVar(&fJobID, "job-id", "", "Migration job ID returned by 'migrate-structure' or 'migrate-email-subscribers'. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1047,7 +1085,7 @@ func genStatusPagesSubscriberExportCmd() *cobra.Command { var fComponentIDs []string var fPageID int64 cmd := &cobra.Command{ - Use: "subscriber-export", + Use: "subscriber-export ", Short: "Export subscribers", Long: `Export subscribers. @@ -1059,16 +1097,21 @@ Request fields: --component-ids []string — Optional component IDs to filter subscribers by. --page-id int (required) — Status page ID. `, + Args: requireExactArg("page_id"), Example: ` flashduty status-page subscriber-export --data '{"page_id":5750613685214}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "page_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("component-ids") { body["component_ids"] = fComponentIDs } if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } + return nil }) if err != nil { return err @@ -1087,7 +1130,7 @@ Request fields: } cmd.Flags().StringSliceVar(&fComponentIDs, "component-ids", nil, "Optional component IDs to filter subscribers by.") cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Status page ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1096,7 +1139,7 @@ func genStatusPagesSubscriberImportCmd() *cobra.Command { var fMethod string var fPageID int64 cmd := &cobra.Command{ - Use: "subscriber-import", + Use: "subscriber-import ", Short: "Import subscribers", Long: `Import subscribers. @@ -1114,16 +1157,21 @@ Request fields: - locale (string) — Preferred locale for notifications. Defaults to the request locale when omitted. - recipient (string) (required) — Email address (for public pages) or user ID (for internal pages). (≤255 chars) `, + Args: requireExactArg("page_id"), Example: ` flashduty status-page subscriber-import --data '{"method":"email","page_id":5750613685214,"subscribers":[{"all":true,"locale":"en-US","recipient":"alice@example.com"},{"all":false,"component_ids":["01KC3GAZ6ZJE40H55GM31RPWZE"],"locale":"zh-CN","recipient":"bob@example.com"}]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "page_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("method") { body["method"] = fMethod } if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } + return nil }) if err != nil { return err @@ -1146,7 +1194,7 @@ Request fields: } cmd.Flags().StringVar(&fMethod, "method", "", "Subscription method. 'email' is only valid for public pages; 'im' is only valid for internal pages. (required) [email, im]") cmd.Flags().Int64Var(&fPageID, "page-id", 0, "Target status page ID. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -1157,7 +1205,7 @@ func genStatusPagesSubscriberListCmd() *cobra.Command { var fP int64 var fLimit int64 cmd := &cobra.Command{ - Use: "subscriber-list", + Use: "subscriber-list ", Short: "List status page subscribers", Long: `List status page subscribers. @@ -1189,9 +1237,13 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - recipient (string) (required) — Subscriber recipient: email address for public pages, user ID for internal pages. - total (integer) (required) — Total matching subscribers. `, + Args: requireExactArg("page_id"), RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "page_id", "int"); err != nil { + return err + } if cmd.Flags().Changed("page-id") { body["page_id"] = fPageID } @@ -1204,6 +1256,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("limit") { body["limit"] = fLimit } + return nil }) if err != nil { return err @@ -1224,7 +1277,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fComponentIDs, "component-ids", "", "Comma-separated component IDs to filter subscribers by.") cmd.Flags().Int64Var(&fP, "page", 0, "Page number (1-based). (min 1)") cmd.Flags().Int64Var(&fLimit, "limit", 0, "Page size (1-100). (1-100)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cli/zz_generated_teams.go b/internal/cli/zz_generated_teams.go index 08219b4..1147e42 100644 --- a/internal/cli/zz_generated_teams.go +++ b/internal/cli/zz_generated_teams.go @@ -45,7 +45,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty team info --data '{"team_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("ref-id") { body["ref_id"] = fRefID } @@ -55,6 +55,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-name") { body["team_name"] = fTeamName } + return nil }) if err != nil { return err @@ -74,7 +75,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team ID.") cmd.Flags().StringVar(&fTeamName, "team-name", "", "Team name.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -82,7 +83,7 @@ func genTeamsReadInfosCmd() *cobra.Command { var dataJSON string var fTeamIDs []int cmd := &cobra.Command{ - Use: "infos", + Use: "infos [...]", Short: "Batch get teams", Long: `Batch get teams. @@ -99,13 +100,18 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; - team_id (integer) - team_name (string) `, + Args: requireArgs("team_ids"), Example: ` flashduty team infos --data '{"team_ids":[1001,1002]}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { + if err := genFoldPositional(args, body, "team_ids", "intslice"); err != nil { + return err + } if cmd.Flags().Changed("team-ids") { body["team_ids"] = fTeamIDs } + return nil }) if err != nil { return err @@ -123,7 +129,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; }, } cmd.Flags().IntSliceVar(&fTeamIDs, "team-ids", nil, "List of team IDs to look up. Max 100. (required)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -176,7 +182,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; Example: ` flashduty team list --data '{"asc":false,"limit":20,"orderby":"created_at","p":1}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("page") { body["p"] = fP } @@ -198,6 +204,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; if cmd.Flags().Changed("query") { body["query"] = fQuery } + return nil }) if err != nil { return err @@ -221,7 +228,7 @@ Response fields ('data' envelope is unwrapped — rows are nested under items[]; cmd.Flags().StringVar(&fOrderby, "orderby", "", "Sort field. [created_at, updated_at, team_name]") cmd.Flags().Int64Var(&fPersonID, "person-id", 0, "Filter by member ID — return only teams this person belongs to.") cmd.Flags().StringVar(&fQuery, "query", "", "Substring match on team name.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -247,7 +254,7 @@ Request fields: Example: ` flashduty team delete --data '{"team_id":1001}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("ref-id") { body["ref_id"] = fRefID } @@ -257,6 +264,7 @@ Request fields: if cmd.Flags().Changed("team-name") { body["team_name"] = fTeamName } + return nil }) if err != nil { return err @@ -280,7 +288,7 @@ Request fields: cmd.Flags().StringVar(&fRefID, "ref-id", "", "External reference ID.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team ID.") cmd.Flags().StringVar(&fTeamName, "team-name", "", "Team name.") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } @@ -322,7 +330,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le Example: ` flashduty team upsert --data '{"description":"Backend reliability engineering team","person_ids":[80011,80012],"team_name":"Backend SRE"}'`, RunE: func(cmd *cobra.Command, args []string) error { return runCommand(cmd, args, func(ctx *RunContext) error { - body, err := genAssembleBody(dataJSON, func(body map[string]any) { + body, err := genAssembleBody(dataJSON, func(body map[string]any) error { if cmd.Flags().Changed("country-code") { body["countryCode"] = fCountryCode } @@ -350,6 +358,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le if cmd.Flags().Changed("team-name") { body["team_name"] = fTeamName } + return nil }) if err != nil { return err @@ -375,7 +384,7 @@ Response fields ('data' envelope is unwrapped — these fields are at the top le cmd.Flags().BoolVar(&fResetIfNameExist, "reset-if-name-exist", false, "If true and a team with the same name already exists, reset its membership to the provided person_ids.") cmd.Flags().Int64Var(&fTeamID, "team-id", 0, "Team ID. Omit or set to 0 to create a new team.") cmd.Flags().StringVar(&fTeamName, "team-name", "", "Team display name. 1–39 characters. (required) (1-39 chars)") - cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.") + cmd.Flags().StringVar(&dataJSON, "data", "", "Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.") return cmd } diff --git a/internal/cmd/cligen/main.go b/internal/cmd/cligen/main.go index f37e1e5..6bd148c 100644 --- a/internal/cmd/cligen/main.go +++ b/internal/cmd/cligen/main.go @@ -677,6 +677,108 @@ func scalarKind(t reflect.Type) (string, bool) { return "", false } +// ---- positional argument selection ----------------------------------------- + +// positionalOverride maps an operationId to the wire name that should become the +// command's positional argument, consulted BEFORE the *_id/*_ids heuristic. It +// exists for ops where the heuristic would pick the wrong field: +// - incidentMerge has both a required scalar target_incident_id and a required +// array source_incident_ids; array-wins would pick the sources, but the +// merge TARGET is the natural positional (sources stay a --source-incident-ids +// flag). +// - incidentWarRoomDetail / incident-write-add-war-room-member each have two +// required scalar *_id fields (chat_id + integration_id); the chat is the +// subject, so pin chat_id rather than leave it ambiguous (and skipped). +// - monit-rule-write-move: the body has a required `ids` field (not matching +// *_ids) for the rules to move, and a required `dest_folder_id` scalar for the +// destination. The heuristic sees only dest_folder_id (single required *_id) +// and picks it, making the command feel like `rule-move ` while `--ids` +// carries the actual subjects. Suppress the positional entirely so both fields +// are explicit flags. +// +// An empty string in this map means "suppress positional" — no positional is +// emitted for that operation, even when the heuristic would pick one. +var positionalOverride = map[string]string{ + "incidentMerge": "target_incident_id", + "incidentWarRoomDetail": "chat_id", + "incident-write-add-war-room-member": "chat_id", + "monit-rule-write-move": "", // suppress: `ids` bypasses *_ids heuristic; dest_folder_id is not the natural subject +} + +// positional describes the positional argument a generated command exposes. +type positional struct { + Wire string // request-body wire key the positional folds into + Kind string // "string" | "slice" | "int" — selects genFoldPositional behavior + Array bool // true => variadic (>=1 arg); false => exactly one arg +} + +// selectPositional decides which (if any) request field becomes the command's +// positional argument. These ops carry the id in the BODY (no path params), so +// the positional must map onto an already-emitted body-field flag (a scalar from +// reqFields). Selection rule: +// +// 1. An operationId in positionalOverride wins outright. +// 2. Otherwise the candidates are REQUIRED body fields whose wire is *_id +// (scalar) or *_ids (array). A single required *_ids array wins (array-wins: +// it also absorbs a co-present scalar id, e.g. incident_id + person_ids -> +// person_ids). With no array and a single required *_id scalar, that scalar +// wins. +// 3. Anything ambiguous — multiple required arrays, or (no override) multiple +// required scalar ids such as channel_id + rule_id — emits NO positional, so +// a wrong guess can never shadow the right --flag. Such commands are still +// fully driveable by their typed flags. +// +// kind/array are read off the emitted flag type (reqFields' scalarKind), so the +// runtime fold matches the flag the user could otherwise set. +func selectPositional(o specOp, scalars []scalarField, byWire map[string]specField) (positional, bool) { + mk := func(wire string) (positional, bool) { + for _, sf := range scalars { + if sf.Wire != wire { + continue + } + switch sf.Kind { + case "[]string": + return positional{Wire: wire, Kind: "slice", Array: true}, true + case "[]int": + return positional{Wire: wire, Kind: "intslice", Array: true}, true + case "int", "float": + return positional{Wire: wire, Kind: "int"}, true + default: // string, bool — treat as string scalar at the CLI surface + return positional{Wire: wire, Kind: "string"}, true + } + } + return positional{}, false // chosen field is not a flag-able scalar; skip + } + + if wire, ok := positionalOverride[o.OpID]; ok { + if wire == "" { + return positional{}, false // explicit suppress: empty string means no positional + } + return mk(wire) + } + + var reqScalars, reqArrays []string + for _, sf := range scalars { + if !byWire[sf.Wire].Required { + continue + } + switch { + case strings.HasSuffix(sf.Wire, "_ids") && (sf.Kind == "[]string" || sf.Kind == "[]int"): + reqArrays = append(reqArrays, sf.Wire) + case strings.HasSuffix(sf.Wire, "_id"): + reqScalars = append(reqScalars, sf.Wire) + } + } + switch { + case len(reqArrays) == 1: + return mk(reqArrays[0]) // array-wins (absorbs any co-present scalar id) + case len(reqArrays) == 0 && len(reqScalars) == 1: + return mk(reqScalars[0]) + default: + return positional{}, false // none or ambiguous → no positional + } +} + // ---- emission -------------------------------------------------------------- func emitService(s service, sdk map[string]map[string]methodInfo, groupShorts map[string]string) (string, int, []string) { @@ -766,10 +868,48 @@ func emitCmd(fn string, s service, o specOp, mi methodInfo) string { // Command literal. The leaf verb must match the name registered in // emitService (cliVerb), so they share the same derivation. verb := cliVerb(o.Path) + + // Positional argument: these body-id ops carry the id in the request body + // (no path params), so the positional folds into an already-emitted body-field + // flag. The placeholder is appended to Use, an arity validator is added, and + // RunE folds the arg(s) into the body before the flags are stamped. + // + // Create commands never get a positional: their required *_id is a parent/owner + // reference (e.g. team_id on channel create), which is awkward as a positional + // and better supplied as an explicit flag. + pos, hasPos := selectPositional(o, scalars, specByWire) + if verb == "create" { + hasPos = false + } + + use := verb + if hasPos { + // Scalar: . Array: the first placeholder is the SINGULAR id + // (, the *_ids wire minus its trailing "s") followed by + // [...] for the additional variadic ids, which reads as a list of one + // id-kind rather than " id2 id3". + name := kebab(pos.Wire) + if pos.Array { + use = verb + " <" + strings.TrimSuffix(name, "s") + "> [...]" + } else { + use = verb + " <" + name + ">" + } + } fmt.Fprintf(&b, "\tcmd := &cobra.Command{\n") - fmt.Fprintf(&b, "\t\tUse: %q,\n", verb) + fmt.Fprintf(&b, "\t\tUse: %q,\n", use) fmt.Fprintf(&b, "\t\tShort: %q,\n", oneLine(o.Summary)) fmt.Fprintf(&b, "\t\tLong: %s,\n", quoteMultiline(longHelp(o, scalars, complexFields, specByWire))) + if hasPos { + // Scalar positionals use requireExactArg so extra arguments (e.g. + // `incident info id1 id2`) are rejected with a clear error instead of + // silently dropping id2. Array positionals use requireArgs (>=1) because + // they are variadic by design. + if pos.Array { + fmt.Fprintf(&b, "\t\tArgs: requireArgs(%q),\n", pos.Wire) + } else { + fmt.Fprintf(&b, "\t\tArgs: requireExactArg(%q),\n", pos.Wire) + } + } if ex := exampleHelp(o); ex != "" { fmt.Fprintf(&b, "\t\tExample: %s,\n", quoteMultiline(ex)) } @@ -785,8 +925,14 @@ func emitCmd(fn string, s service, o specOp, mi methodInfo) string { parsedTimeVar(sf.Wire), okTimeVar(sf.Wire), flagName(sf.Wire), flagVar(sf.Wire)) b.WriteString("\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") } - // body assembly - b.WriteString("\t\t\t\tbody, err := genAssembleBody(dataJSON, func(body map[string]any) {\n") + // body assembly. The positional argument folds in first (after --data, before + // the typed flags) so an explicitly-set flag for the same field overrides it, + // matching genAssembleBody's --data-then-flags overlay order. genFoldPositional + // can fail (int parse); the error propagates directly via the callback's return. + b.WriteString("\t\t\t\tbody, err := genAssembleBody(dataJSON, func(body map[string]any) error {\n") + if hasPos { + fmt.Fprintf(&b, "\t\t\t\t\tif err := genFoldPositional(args, body, %q, %q); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n", pos.Wire, pos.Kind) + } for _, sf := range scalars { if isTime[sf.Wire] { fmt.Fprintf(&b, "\t\t\t\t\tif %s {\n\t\t\t\t\t\tbody[%q] = %s\n\t\t\t\t\t}\n", @@ -796,6 +942,7 @@ func emitCmd(fn string, s service, o specOp, mi methodInfo) string { fmt.Fprintf(&b, "\t\t\t\t\tif cmd.Flags().Changed(%q) {\n\t\t\t\t\t\tbody[%q] = %s\n\t\t\t\t\t}\n", flagName(sf.Wire), sf.Wire, flagVar(sf.Wire)) } + b.WriteString("\t\t\t\t\treturn nil\n") b.WriteString("\t\t\t\t})\n") b.WriteString("\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n") @@ -838,7 +985,7 @@ func emitCmd(fn string, s service, o specOp, mi methodInfo) string { fmt.Fprintf(&b, "\tcmd.Flags().%s(&%s, %q, %s, %q)\n", flagSetter(sf.Kind), flagVar(sf.Wire), flagName(sf.Wire), flagZero(sf.Kind), usage) } - b.WriteString("\tcmd.Flags().StringVar(&dataJSON, \"data\", \"\", \"Full request body as JSON; typed flags override its fields. Accepts inline JSON, or - to read stdin.\")\n") + b.WriteString("\tcmd.Flags().StringVar(&dataJSON, \"data\", \"\", \"Full request body as JSON; positional arguments and typed flags override its fields. Accepts inline JSON, or - to read stdin.\")\n") b.WriteString("\treturn cmd\n}\n\n") return b.String() } diff --git a/internal/cmd/cligen/positional_test.go b/internal/cmd/cligen/positional_test.go new file mode 100644 index 0000000..8378fdd --- /dev/null +++ b/internal/cmd/cligen/positional_test.go @@ -0,0 +1,109 @@ +package main + +import "testing" + +// mkOp returns a minimal specOp with the given OpID for use in selectPositional tests. +func mkOp(opID string) specOp { return specOp{OpID: opID} } + +// mkField returns a specField for byWire construction. +func mkField(wire string, required bool) specField { return specField{Wire: wire, Required: required} } + +// byWireOf builds the byWire map from a list of specFields. +func byWireOf(fields ...specField) map[string]specField { + m := map[string]specField{} + for _, f := range fields { + m[f.Wire] = f + } + return m +} + +func TestSelectPositional(t *testing.T) { + cases := []struct { + name string + opID string + scalars []scalarField + byWire map[string]specField + wantWire string + wantOK bool + }{ + { + // Override-map hit wins over heuristic. + name: "override map hit", + opID: "incidentMerge", + scalars: []scalarField{{Wire: "target_incident_id", Kind: "string"}, {Wire: "source_incident_ids", Kind: "[]string"}}, + byWire: byWireOf(mkField("target_incident_id", true), mkField("source_incident_ids", true)), + wantWire: "target_incident_id", + wantOK: true, + }, + { + // Empty-string override → suppress positional. + name: "empty override suppresses", + opID: "monit-rule-write-move", + scalars: []scalarField{{Wire: "dest_folder_id", Kind: "int"}, {Wire: "ids", Kind: "[]int"}}, + byWire: byWireOf(mkField("dest_folder_id", true), mkField("ids", true)), + wantOK: false, + }, + { + // Single required *_ids array wins over a co-present required *_id scalar. + name: "array wins over scalar", + opID: "someOp", + scalars: []scalarField{{Wire: "incident_id", Kind: "string"}, {Wire: "person_ids", Kind: "[]string"}}, + byWire: byWireOf(mkField("incident_id", true), mkField("person_ids", true)), + wantWire: "person_ids", + wantOK: true, + }, + { + // Single required *_id scalar when no array. + name: "single scalar id", + opID: "someOp", + scalars: []scalarField{{Wire: "incident_id", Kind: "string"}, {Wire: "limit", Kind: "int"}}, + byWire: byWireOf(mkField("incident_id", true), mkField("limit", false)), + wantWire: "incident_id", + wantOK: true, + }, + { + // Ambiguous: two required scalars → no positional. + name: "ambiguous two scalars", + opID: "someOp", + scalars: []scalarField{{Wire: "channel_id", Kind: "string"}, {Wire: "rule_id", Kind: "string"}}, + byWire: byWireOf(mkField("channel_id", true), mkField("rule_id", true)), + wantOK: false, + }, + { + // Ambiguous: two required arrays → no positional. + name: "ambiguous two arrays", + opID: "someOp", + scalars: []scalarField{{Wire: "person_ids", Kind: "[]string"}, {Wire: "team_ids", Kind: "[]string"}}, + byWire: byWireOf(mkField("person_ids", true), mkField("team_ids", true)), + wantOK: false, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + got, ok := selectPositional(mkOp(c.opID), c.scalars, c.byWire) + if ok != c.wantOK { + t.Fatalf("ok=%v, want %v", ok, c.wantOK) + } + if ok && got.Wire != c.wantWire { + t.Errorf("Wire=%q, want %q", got.Wire, c.wantWire) + } + }) + } +} + +func TestSelectPositionalCreateVerbSuppressed(t *testing.T) { + // Create-verb suppression is handled in emitCmd (not selectPositional), so + // selectPositional itself still returns a result for a "create" op that has a + // single required *_id field. This test documents that contract: the suppression + // layer above selectPositional is responsible for the create-verb check. + scalars := []scalarField{{Wire: "team_id", Kind: "int"}} + byWire := byWireOf(mkField("team_id", true)) + _, ok := selectPositional(mkOp("channelCreate"), scalars, byWire) + // team_id ends in _id and is required — heuristic would pick it. + // (emitCmd suppresses it because verb=="create"; selectPositional is unaware.) + if !ok { + t.Log("selectPositional returned no positional for channelCreate (team_id); " + + "emitCmd create-verb suppression is redundant here but still correct") + } +}