feat(app-scopes): add brevo app scopes update for M2M apps [BEX-486] - #111
feat(app-scopes): add brevo app scopes update for M2M apps [BEX-486]#111shubham773 wants to merge 4 commits into
brevo app scopes update for M2M apps [BEX-486]#111Conversation
Machine-to-machine apps had no way to change their granted scopes after creation. Adds `brevo app scopes update --app-id <id> --scopes <a,b,c> --mode append|replace`, which reads the app's current scopes for a confirmation preview, then sends the requested scopes plus the mode to a new scopes-only PATCH endpoint so the server performs the actual append/replace. Supporting changes: - `SubcommandGroupDefinition` gains an optional `groups` field so `command-registry.ts` can register one level of subcommand nesting (`app scopes update`), without reshaping `CommandDefinition`. - `services/app.ts` gains `updateAppScopes` and a heuristic `isM2mApp` detector (no server-sent discriminator exists yet). - Corrects several "M2M scopes are fixed at creation" messages and code comments (in `en.ts`, `create.ts`, `scope-prompts.ts`, and both agent docs) that this command makes untrue, and extracts the typed-scope fallback prompt into `scope-prompts.ts` so `create` and this command share one implementation. The backend scopes-update API this depends on had not shipped at the time of this change — the request/response shape is built from that API's own design and is called out as unverified (comments in constants.ts/services/app.ts, changeset, agent docs) pending a follow-up once it lands. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… scope selector
Drop the `--mode append|replace` flag from `brevo app scopes update` in
favor of pre-filling the interactive scope picker (and its typed
fallback) with the app's current scopes. Whatever a partner submits —
scopes ticked/unticked from that starting point, or edited text —
already is the complete desired scope set, so the CLI always sends a
plain `{ scopes }` replace and the server never has to reconcile a
partial add/remove delta. `--scopes`, passed directly, keeps the same
contract non-interactively: it's the full desired list, not a delta.
Simplifies the endpoint contract back to a plain replace (removing the
CLI-invented `mode` field), and updates messages, docs, and tests
accordingly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…BEX-486] Adds two steps to private-app.ts's M2M lifecycle: an update that adds a scope (verified via a fresh `app credentials` read, not just the command's own echoed output) and a no-op resubmit of the same scopes that must not prompt for confirmation. Gated by a new `m2m-scopes-update` capability (core.ts), detected the same way `m2m-flag` is (a help-text probe), so a `--against=published` run against an npm release that predates this command skips rather than fails. It also downgrades itself at runtime if the call fails — the scopes-update backend (BEX-481) had not shipped as of this PR, so an environment where it's still unavailable server-side skips with `markFeatureUnavailable`, the same pattern `public-distribution` uses for a build that offers the flag but whose environment declines it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
piyushsarin-sib
left a comment
There was a problem hiding this comment.
Review notes on the app scopes update surface. One blocker (the --json gate), a couple of correctness items, and some copy/coverage nits — details inline.
One item that isn't on a changed line: README.md still says "Scopes are fixed at creation (there is no config to edit…)" and the command table doesn't list brevo app scopes update. Per CLAUDE.md the README update belongs on the chore(release) PR, so flagging it for there rather than asking for it here.
|
Manually Review: Following on from the comment above — an M2M app has no If the backend permits scope mutation on M2M auths only, then upload's scope path can never do anything except produce a server-side rejection. So I'd suggest blocking it there too:
What do you think — hard block in upload, or keep it and only warn until the backend behaviour is confirmed? |
For m2m apps, scopes wont be allowed to be updated via upload command/API. That access control will be added in a separate PR. |
- scopes-update.ts: `--json` alone now skips confirmation too (was missing the `!options.json` half of the gate, so --json without --yes printed a human diff and then died on inquirer off a TTY). - scopes-update.ts: refuse with a message naming `--scopes`, not `--app-id`, when scopes can't be prompted for non-interactively — the app was already named by that point. - scopes-update.ts: fall back to the fetched app's own name for the confirm-prompt label when `--app-id` was passed directly (picker-only label previously left it blank, showing the ID twice). - services/app.ts: stop running the scopes-PATCH response through `normalizeAppId` — the unverified response shape could plausibly omit `app_id`, and the caller only ever reads `.scopes`, so a successful update should never fail over a field nobody uses. - scope-prompts.ts: only pre-check a scope-picker heading under the cascading prompt; the plain-checkbox fallback has no cascade wiring, so a pre-checked heading there silently re-adds a scope the partner just unticked. - scope-prompts.ts: correct the typed-fallback prompt's behavior/copy — inquirer 8's `input` prompt does not pre-populate an editable line from `default` (verified against inquirer's own source), so typing anything discards the prefill entirely. Adds an explicit intro message stating the current scopes and that Enter alone keeps them. - scripts/smoke/private-app.ts: narrow the scopes-update step's catch to the "endpoint not there yet" shape (404/405/501-ish) and rethrow everything else, so a genuine regression can't report as a skip. Regression tests added for all of the above. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|



Summary
brevo app scopes update --app-id <id> --scopes <a,b,c> [--yes] [--json]to change an existing M2M app's granted OAuth scopes — previously M2M scopes were fixed at creation with no way to edit them.--yes; a request that changes nothing is a no-op (exit0, no network call).command-registry.tswith an optional one-level-deepergroupsfield soapp scopes updatecan exist without reshaping the flatCommandDefinitionmodel.en.ts,create.ts,scope-prompts.ts, both agent docs, the pending M2M changeset) that this command makes untrue, and extracts the typed-scope fallback prompt intoscope-prompts.tssoapp createand this command share one implementation.private-app.ts's M2M lifecycle — an update that adds a scope (verified via a freshapp credentialsread, not just the command's own echoed output) and a no-op resubmit that must not prompt for confirmation. Gated by a newm2m-scopes-updatecapability (core.ts), detected the same waym2m-flagis, so--against=publishedskips cleanly on an npm release that predates this command — and it self-downgrades at runtime (markFeatureUnavailable) if the backend call fails, the same patternpublic-distributionuses.Test plan
yarn lint— cleanyarn test:ci— 74 suites / 1698 tests passing, including coverage forisM2mApp,updateAppScopes, nested command registration, and the fullscopes updatecommand flow (pre-filled picker/typed-prompt, --scopes as full replace, no-op detection, app picker, non-M2M refusal, JSON output, error propagation)yarn buildandPREVIEW=1 yarn build— both greenscripts/smoke/*.tstype-check and lint clean (eslint 'scripts/**/*.ts',prettier --check)brevo app scopes update --help,brevo app scopes --help,app --help, and that--modeis correctly rejected as an unknown option🤖 Generated with Claude Code