Skip to content

feat: add --customer-context / -D flag for per-command context override#6

Open
jtdelia wants to merge 3 commits intomainfrom
feat/per-command-customer-context
Open

feat: add --customer-context / -D flag for per-command context override#6
jtdelia wants to merge 3 commits intomainfrom
feat/per-command-customer-context

Conversation

@jtdelia
Copy link
Copy Markdown

@jtdelia jtdelia commented Apr 1, 2026

Summary

Adds a --customer-context (-D) flag to all API commands, allowing the active customer context to be overridden on a per-command basis without changing the persistent context set via dci customer-context set.

dci list-reports --customer-context acme.com
dci list-budgets -D acme.com --output json

The flag overrides the active context from the config file or DCI_CUSTOMER_CONTEXT env var for the duration of that command only. Default behavior for all users is unchanged.

Test plan

  • go build ./... passes
  • --customer-context <domain> returns correct results for target customer
  • -D <domain> short form works
  • Flag works across all command groups
  • --customer-context "" returns a clear error
  • Doer hint suppressed when --customer-context is used and a 403 occurs

Copilot AI review requested due to automatic review settings April 1, 2026 22:16
@jtdelia jtdelia requested a review from apgiorgi as a code owner April 1, 2026 22:16
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a per-command customer context override flag (--customer-context / -D) to the DCI CLI so users can target a specific customer context for a single invocation without changing the persisted context.

Changes:

  • Introduces a new persistent --customer-context / -D flag on API commands.
  • Overrides customerContext in Restish query params (rsh-query) when the flag is explicitly provided, including validation for empty values.
  • Suppresses the “Doer needs a customer context” hint when a per-command override was provided.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +34 to +36
// customerContextFlagValue holds the --customer-context / -D flag value when
// set, used to suppress the Doer hint even when no persistent context file exists.
var customerContextFlagValue string
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

customerContextFlagValue introduces package-level mutable state that is only set in PersistentPreRunE and never reset. If run() is ever invoked multiple times in-process (e.g., future tests or embedding), this can incorrectly suppress the Doer hint on subsequent runs. Consider resetting it at the start of run() or avoiding the global by plumbing the flag state into maybeHintDoerContext (e.g., pass a boolean/string).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the follow-up commit. customerContextFlagValue is now reset to "" at the top of run() before any other logic executes, so repeated in-process calls always start with a clean slate.

Comment on lines +1029 to +1045
// If --customer-context / -D was explicitly passed, override whatever
// applyCustomerContext() injected from the file or env var.
if flag := cmd.Flags().Lookup("customer-context"); flag != nil && flag.Changed {
val := strings.TrimSpace(flag.Value.String())
if val == "" {
return fmt.Errorf("--customer-context requires a non-empty domain name")
}
existing := viper.GetStringSlice("rsh-query")
filtered := existing[:0]
for _, q := range existing {
if !strings.HasPrefix(q, "customerContext=") {
filtered = append(filtered, q)
}
}
viper.Set("rsh-query", append(filtered, "customerContext="+val))
customerContextFlagValue = val
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New --customer-context/-D behavior is implemented here (validation, precedence over env/file via rsh-query, and Doer-hint suppression), but there are no corresponding unit tests. Since this repo already has main_test.go covering related customer-context behavior, please add tests for: (1) --customer-context "" errors, (2) flag overrides env/file customerContext, and (3) Doer hint is suppressed when the flag is used and a 403 occurs.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added TestCustomerContextFlag in the follow-up commit covering: (1) --customer-context "" exits non-zero with the expected error message, (2) -D / --customer-context appears in command help output, and (3) customerContextFlagValue being set correctly suppresses the Doer hint. The 403 hint suppression is tested at the unit level by directly asserting the guard condition, since cli.GetLastStatus() is unexported and can't be set from outside the restish package in tests.

Copy link
Copy Markdown
Collaborator

@apgiorgi apgiorgi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again, John!

Copy link
Copy Markdown
Collaborator

@apgiorgi apgiorgi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three issues — two blocking.

main_test.go Outdated
// Since GetLastStatus() returns 0 (no HTTP call made), maybeHintDoerContext
// will return at the status check before reaching our guard — so we test
// the guard in isolation by confirming the global is respected.
if readCustomerContext(dir) != "" {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] This test never invokes maybeHintDoerContext()GetLastStatus() returns 0 and the function exits before reaching the suppression guard. The test only asserts that a global was set, which cannot detect a regression. Please restructure so maybeHintDoerContext() is called with a 403 status and assert that stderr is empty when the flag is set.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Refactored maybeHintDoerContext to accept status int as a parameter instead of calling cli.GetLastStatus() internally. The test now calls maybeHintDoerContext(1, 403, dir) directly with a Doer JWT, no persistent context file, and customerContextFlagValue set — then asserts stderr is empty. This properly exercises the suppression guard rather than just asserting the global was set.

main_test.go Outdated
t.Errorf("customerContext = %q, want %q", ctx, tt.wantContext)
}
})
if ctx := readCustomerContext(dir); ctx != tt.wantContext {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[must fix] Indentation regression — this block and its closing }) are de-indented by one level. Run gofmt -w main_test.go.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Ran gofmt -w main_test.go — the indentation regression was introduced during editing. Both files now pass gofmt -l cleanly.

dciCmd.PersistentFlags().StringP("table-columns", "C", "", "Comma-separated list of columns to include (default: all)")
dciCmd.PersistentFlags().IntP("table-width", "W", 0, "Table width in columns (default: auto-detect terminal width)")
dciCmd.PersistentFlags().IntP("table-max-col-width", "X", 0, "Maximum width per column when fitting or wrapping (0 = auto)")
dciCmd.PersistentFlags().StringP("customer-context", "D", "", "Override the active customer context for this command (e.g. acme.com)")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[consider] Since --customer-context is registered on PersistentFlags() of the root command, it silently appears on customer-context set/clear/show where it does nothing. Consider documenting the no-op behavior or filtering these sub-commands.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified — the flag does not appear on customer-context set/clear/show. The --customer-context flag is registered on dciCmd.PersistentFlags(), where dciCmd is the restish API subcommand (a child of cli.Root). The customer-context command is registered directly on cli.Root as a sibling of dciCmd, not a child. Cobra persistent flags only propagate downward to children, not sideways to siblings, so there is no leakage. Confirmed by running dci customer-context --help — only -h/--help appears.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants