Skip to content

Commit 80aa373

Browse files
committed
adding operations command support for ODS - claude
1 parent b74d199 commit 80aa373

10 files changed

Lines changed: 916 additions & 7 deletions

File tree

.changeset/ods-enhancements.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@salesforce/b2c-cli': minor
3+
'@salesforce/b2c-dx-docs': patch
4+
---
5+
6+
ODS CLI: **`b2c sandbox create`** adds **`--emails`** for notification addresses; **`b2c sandbox update`** adds **`--start-scheduler`** and **`--stop-scheduler`** (JSON or `"null"` to clear); **`b2c realm update`** adds **`--emails`** and **`--local-users-allowed`** / **`--no-local-users-allowed`**.
7+
8+
Sandbox API: **`b2c sandbox operations list`** and **`b2c sandbox operations get`** (inspect lifecycle operations); **`b2c sandbox alias get`** (get one alias by ID, same endpoint as **`alias list --alias-id`**).
9+
10+
User guide updated for scheduling flags, sandbox operations, and **`b2c sandbox alias get`**.

.changeset/ods-scheduling-email-local-users.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/cli/sandbox.md

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ These commands were previously available as `b2c ods <command>`. The `ods` prefi
1212

1313
## Sandbox ID Formats
1414

15-
Commands that operate on a specific sandbox (`get`, `update`, `start`, `stop`, `restart`, `delete`) accept two ID formats:
15+
Commands that operate on a specific sandbox (`get`, `update`, `start`, `stop`, `restart`, `delete`, `operations list`, `operations get`) accept two ID formats:
1616

1717
| Format | Example | Description |
1818
|--------|---------|-------------|
@@ -392,6 +392,106 @@ b2c sandbox restart zzzv_123 --json
392392

393393
---
394394

395+
## b2c sandbox operations list {#b2c-sandbox-operations-list}
396+
397+
List past and current **operations** on a sandbox (for example start, stop, restart, reset, create, delete, upgrade). This maps to the ODS API `GET /sandboxes/{sandboxId}/operations` endpoint.
398+
399+
To **request** a lifecycle operation (`start`, `stop`, `restart`, `reset`), use `b2c sandbox start|stop|restart|reset` instead; those commands call `POST /sandboxes/{sandboxId}/operations`.
400+
401+
### Usage
402+
403+
```bash
404+
b2c sandbox operations list <SANDBOXID>
405+
```
406+
407+
### Arguments
408+
409+
| Argument | Description | Required |
410+
|----------|-------------|----------|
411+
| `SANDBOXID` | Sandbox ID (UUID or realm-instance, e.g., `zzzv-123`) | Yes |
412+
413+
### Flags
414+
415+
| Flag | Short | Description | Default |
416+
|------|-------|-------------|---------|
417+
| `--from` | | Earliest operation time (ISO 8601). If omitted, the API defaults to roughly the last 30 days. | |
418+
| `--to` | | Latest operation time (ISO 8601). If omitted, the API defaults to now. | |
419+
| `--operation-state` | | Filter by lifecycle state: `pending`, `running`, or `finished` | |
420+
| `--status` | | Filter finished operations by outcome: `success` or `failure` | |
421+
| `--operation` | | Filter by operation type: `start`, `stop`, `restart`, `reset`, `create`, `delete`, `upgrade` | |
422+
| `--sort-order` | | Sort order: `asc` or `desc` | |
423+
| `--sort-by` | | Sort field: `created`, `operation_state`, `status`, or `operation` | |
424+
| `--page` | | Page index (0-based) | |
425+
| `--per-page` | | Page size (API default is typically 20) | |
426+
| `--columns`, `-c` | | Columns to display (comma-separated); see **Available columns** below | |
427+
| `--extended`, `-x` | | Include extended columns (for example `operationBy`) | `false` |
428+
429+
### Available columns
430+
431+
`id`, `operation`, `operationState`, `status`, `sandboxState`, `createdAt`, `operationBy` (extended)
432+
433+
**Default columns:** `operation`, `operationState`, `status`, `sandboxState`, `createdAt`, `id`
434+
435+
### Examples
436+
437+
```bash
438+
# List recent operations for a sandbox
439+
b2c sandbox operations list zzzv-123
440+
441+
# Only finished operations
442+
b2c sandbox operations list zzzv-123 --operation-state finished
443+
444+
# Date range and paging
445+
b2c sandbox operations list zzzv-123 --from 2025-01-01 --to 2025-12-31 --page 0 --per-page 50
446+
447+
# Custom columns
448+
b2c sandbox operations list zzzv-123 --columns operation,operationState,status,createdAt
449+
450+
# Full API response (includes paging metadata when present)
451+
b2c sandbox operations list zzzv-123 --json
452+
```
453+
454+
### Output
455+
456+
When not using `--json`, the command prints a one-line paging summary when metadata is present, then a table of operations. Use `--json` to inspect `metadata` (page, totals) and the raw `data` array.
457+
458+
---
459+
460+
## b2c sandbox operations get {#b2c-sandbox-operations-get}
461+
462+
Return details for a **single** sandbox operation by its operation UUID (maps to `GET /sandboxes/{sandboxId}/operations/{operationId}`). Use the operation `id` from `b2c sandbox operations list` or from the JSON output of `b2c sandbox start|stop|restart|reset` when using `--json`.
463+
464+
### Usage
465+
466+
```bash
467+
b2c sandbox operations get <SANDBOXID> <OPERATIONID>
468+
```
469+
470+
### Arguments
471+
472+
| Argument | Description | Required |
473+
|----------|-------------|----------|
474+
| `SANDBOXID` | Sandbox ID (UUID or realm-instance, e.g., `zzzv-123`) | Yes |
475+
| `OPERATIONID` | Operation UUID | Yes |
476+
477+
### Examples
478+
479+
```bash
480+
# Show operation details (human-readable)
481+
b2c sandbox operations get zzzv-123 550e8400-e29b-41d4-a716-446655440000
482+
483+
# Operation as JSON (sandbox operation model only)
484+
b2c sandbox operations get zzzv-123 550e8400-e29b-41d4-a716-446655440000 --json
485+
```
486+
487+
### Output
488+
489+
When not using `--json`, the command prints a short **Operation Details** block including operation type, state, outcome, sandbox state, created time, and who ran the operation when available.
490+
491+
With `--json`, the command returns the **operation object** (not the full API envelope), consistent with `b2c sandbox get`.
492+
493+
---
494+
395495
## b2c sandbox delete
396496

397497
Delete an on-demand sandbox.
@@ -696,6 +796,7 @@ Alias commands are available both under the `sandbox` topic and the legacy `ods`
696796

697797
- `b2c sandbox alias create`
698798
- `b2c sandbox alias list`
799+
- `b2c sandbox alias get`
699800
- `b2c sandbox alias delete`
700801

701802
### b2c sandbox alias create
@@ -790,6 +891,39 @@ When listing multiple aliases without `--json`, the command prints a table with:
790891
- Whether the alias is unique
791892
- DNS verification record (if any)
792893

894+
For **one alias** by ID, you can also use **`b2c sandbox alias get`** (same API as `list --alias-id`).
895+
896+
### b2c sandbox alias get {#b2c-sandbox-alias-get}
897+
898+
Get details for a **single** hostname alias (ODS API `GET /sandboxes/{sandboxId}/aliases/{sandboxAliasId}`). This is equivalent to `b2c sandbox alias list <SANDBOXID> --alias-id <ALIASID>` but uses positional arguments only.
899+
900+
#### Usage
901+
902+
```bash
903+
b2c sandbox alias get <SANDBOXID> <ALIASID>
904+
```
905+
906+
#### Arguments
907+
908+
| Argument | Description | Required |
909+
|----------|-------------|----------|
910+
| `SANDBOXID` | Sandbox ID (UUID or realm-instance, e.g., `zzzv-123`) | Yes |
911+
| `ALIASID` | Alias UUID | Yes |
912+
913+
#### Examples
914+
915+
```bash
916+
# Human-readable details
917+
b2c sandbox alias get zzzv-123 some-alias-uuid
918+
919+
# Alias object as JSON (same shape as list/get for a single alias)
920+
b2c sandbox alias get zzzv-123 some-alias-uuid --json
921+
```
922+
923+
#### Output
924+
925+
When not using `--json`, the command prints an **Alias Details** section (hostname, status, uniqueness, DNS TXT verification, registration URL when present, optional cookie hint). With `--json`, it returns the **alias object** only.
926+
793927
### b2c sandbox alias delete
794928

795929
Delete a sandbox alias.

packages/b2c-cli/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@
179179
},
180180
"realm": {
181181
"description": "Manage sandbox realms (alias for 'sandbox realm')"
182+
},
183+
"operations": {
184+
"description": "List and inspect sandbox lifecycle operations (alias for 'sandbox operations')"
182185
}
183186
}
184187
},
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (c) 2025, Salesforce, Inc.
3+
* SPDX-License-Identifier: Apache-2
4+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
import {Args, ux} from '@oclif/core';
7+
import cliui from 'cliui';
8+
import {OdsCommand} from '@salesforce/b2c-tooling-sdk/cli';
9+
import {getApiErrorMessage, type OdsComponents} from '@salesforce/b2c-tooling-sdk';
10+
import {t, withDocs} from '../../../i18n/index.js';
11+
12+
type SandboxAliasModel = OdsComponents['schemas']['SandboxAliasModel'];
13+
14+
/**
15+
* Get details for a single sandbox hostname alias (ODS API GET /sandboxes/{sandboxId}/aliases/{sandboxAliasId}).
16+
*/
17+
export default class SandboxAliasGet extends OdsCommand<typeof SandboxAliasGet> {
18+
static aliases = ['ods:alias:get'];
19+
20+
static args = {
21+
sandboxId: Args.string({
22+
description: 'Sandbox ID (UUID or realm-instance, e.g., abcd-123)',
23+
required: true,
24+
}),
25+
aliasId: Args.string({
26+
description: 'Alias UUID',
27+
required: true,
28+
}),
29+
};
30+
31+
static description = withDocs(
32+
t('commands.sandbox.alias.get.description', 'Show details for a specific sandbox hostname alias'),
33+
'/cli/sandbox.html#b2c-sandbox-alias-get',
34+
);
35+
36+
static enableJsonFlag = true;
37+
38+
static examples = [
39+
'<%= config.bin %> <%= command.id %> zzzv-123 alias-uuid-here',
40+
'<%= config.bin %> <%= command.id %> abc12345-1234-1234-1234-abc123456789 alias-uuid-here --json',
41+
];
42+
43+
async run(): Promise<SandboxAliasModel | undefined> {
44+
const sandboxId = await this.resolveSandboxId(this.args.sandboxId);
45+
const {aliasId} = this.args;
46+
47+
this.log(
48+
t('commands.sandbox.alias.get.fetching', 'Fetching alias {{aliasId}} for sandbox {{sandboxId}}...', {
49+
aliasId,
50+
sandboxId: this.args.sandboxId,
51+
}),
52+
);
53+
54+
const result = await this.odsClient.GET('/sandboxes/{sandboxId}/aliases/{sandboxAliasId}', {
55+
params: {
56+
path: {sandboxId, sandboxAliasId: aliasId},
57+
},
58+
});
59+
60+
if (result.error) {
61+
this.error(
62+
t('commands.sandbox.alias.get.error', 'Failed to fetch alias: {{message}}', {
63+
message: getApiErrorMessage(result.error, result.response),
64+
}),
65+
);
66+
}
67+
68+
const alias = result.data?.data;
69+
if (!alias) {
70+
this.log(t('commands.sandbox.alias.get.noData', 'No alias details were returned.'));
71+
return undefined;
72+
}
73+
74+
if (this.jsonEnabled()) {
75+
return alias;
76+
}
77+
78+
this.printAlias(alias);
79+
return alias;
80+
}
81+
82+
private printAlias(alias: SandboxAliasModel): void {
83+
const ui = cliui({width: process.stdout.columns || 80});
84+
ui.div({text: 'Alias Details', padding: [1, 0, 0, 0]});
85+
ui.div({text: '─'.repeat(50), padding: [0, 0, 0, 0]});
86+
87+
const labelWidth = 22;
88+
const rows: [string, string | undefined][] = [
89+
['ID', alias.id],
90+
['Hostname', alias.name],
91+
['Status', alias.status],
92+
['Unique', alias.unique === undefined ? undefined : String(alias.unique)],
93+
['Sandbox ID', alias.sandboxId],
94+
[
95+
'Let’s Encrypt',
96+
alias.requestLetsEncryptCertificate === undefined ? undefined : String(alias.requestLetsEncryptCertificate),
97+
],
98+
['DNS verification (TXT)', alias.domainVerificationRecord],
99+
['Registration URL', alias.registration],
100+
];
101+
102+
for (const [label, value] of rows) {
103+
if (value !== undefined) {
104+
ui.div({text: `${label}:`, width: labelWidth, padding: [0, 2, 0, 0]}, {text: value, padding: [0, 0, 0, 0]});
105+
}
106+
}
107+
108+
if (alias.cookie) {
109+
const c = alias.cookie;
110+
ui.div(
111+
{text: 'Cookie:', width: labelWidth, padding: [0, 2, 0, 0]},
112+
{text: `${c.name}=${c.value}${c.path ? ` (path: ${c.path})` : ''}`, padding: [0, 0, 0, 0]},
113+
);
114+
}
115+
116+
ux.stdout(ui.toString());
117+
}
118+
}

0 commit comments

Comments
 (0)