Skip to content

Commit 8bfeca8

Browse files
authored
@W-21552190: enhance the vs code ext ODS browser (#251)
1 parent 322923e commit 8bfeca8

3 files changed

Lines changed: 89 additions & 3 deletions

File tree

packages/b2c-vs-extension/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@
240240
"icon": "$(link-external)",
241241
"category": "B2C DX - Sandboxes"
242242
},
243+
{
244+
"command": "b2c-dx.sandbox.extendExpiration",
245+
"title": "Extend Expiration",
246+
"icon": "$(clock)",
247+
"category": "B2C DX - Sandboxes"
248+
},
243249
{
244250
"command": "b2c-dx.instance.inspect",
245251
"title": "B2C Instance Config",
@@ -515,6 +521,11 @@
515521
"when": "view == b2cSandboxExplorer && viewItem == sandbox-started",
516522
"group": "2_lifecycle@3"
517523
},
524+
{
525+
"command": "b2c-dx.sandbox.extendExpiration",
526+
"when": "view == b2cSandboxExplorer && viewItem =~ /^sandbox-/",
527+
"group": "2_lifecycle@4"
528+
},
518529
{
519530
"command": "b2c-dx.sandbox.delete",
520531
"when": "view == b2cSandboxExplorer && viewItem =~ /^sandbox-/",
@@ -590,6 +601,10 @@
590601
"command": "b2c-dx.sandbox.openBM",
591602
"when": "false"
592603
},
604+
{
605+
"command": "b2c-dx.sandbox.extendExpiration",
606+
"when": "false"
607+
},
593608
{
594609
"command": "b2c-dx.webdav.newFolder",
595610
"when": "false"

packages/b2c-vs-extension/src/sandbox-tree/sandbox-commands.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,55 @@ export function registerSandboxCommands(
239239
await vscode.env.openExternal(vscode.Uri.parse(`https://${node.sandbox.hostName}/on/demandware.store/Sites-Site`));
240240
});
241241

242+
const extendExpiration = vscode.commands.registerCommand(
243+
'b2c-dx.sandbox.extendExpiration',
244+
async (node: SandboxTreeItem) => {
245+
if (!node) return;
246+
247+
const ttlStr = await vscode.window.showInputBox({
248+
title: `Extend Expiration — ${node.label ?? node.sandbox.id}`,
249+
prompt: 'Hours to add to sandbox lifetime (0 = infinite)',
250+
value: '24',
251+
validateInput: (v) => {
252+
const n = Number(v);
253+
if (Number.isNaN(n) || n < 0) return 'Enter a non-negative number';
254+
return null;
255+
},
256+
});
257+
if (ttlStr === undefined) return;
258+
const ttl = Number(ttlStr);
259+
260+
await vscode.window.withProgress(
261+
{
262+
location: vscode.ProgressLocation.Notification,
263+
title: `Extending expiration for sandbox ${node.sandbox.id}...`,
264+
},
265+
async () => {
266+
try {
267+
const odsClient = getOdsClientFromConfig(configProvider);
268+
const result = await odsClient.PATCH('/sandboxes/{sandboxId}', {
269+
params: {path: {sandboxId: node.sandbox.id}},
270+
body: {ttl},
271+
});
272+
if (result.error) {
273+
vscode.window.showErrorMessage(
274+
`Failed to extend expiration: ${getApiErrorMessage(result.error, result.response)}`,
275+
);
276+
return;
277+
}
278+
const message =
279+
ttl === 0 ? 'Sandbox expiration removed (infinite).' : `Sandbox expiration extended by ${ttl} hours.`;
280+
vscode.window.showInformationMessage(message);
281+
treeProvider.refreshRealm(node.realm);
282+
} catch (err) {
283+
const message = err instanceof Error ? err.message : String(err);
284+
vscode.window.showErrorMessage(`Failed to extend expiration: ${message}`);
285+
}
286+
},
287+
);
288+
},
289+
);
290+
242291
return [
243292
detailRegistration,
244293
refresh,
@@ -251,5 +300,6 @@ export function registerSandboxCommands(
251300
restart,
252301
viewDetails,
253302
openBM,
303+
extendExpiration,
254304
];
255305
}

packages/b2c-vs-extension/src/sandbox-tree/sandbox-tree-provider.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ export class SandboxTreeItem extends vscode.TreeItem {
4545
super(label, vscode.TreeItemCollapsibleState.None);
4646

4747
const state = (sandbox.state ?? 'unknown').toLowerCase();
48-
this.description = state;
48+
const eolDate = sandbox.eol
49+
? new Date(sandbox.eol).toLocaleDateString(undefined, {year: 'numeric', month: 'short', day: 'numeric'})
50+
: undefined;
51+
this.description = eolDate ? `${state} · expires ${eolDate}` : state;
4952
this.iconPath = STATE_ICONS[state] ?? DEFAULT_ICON;
5053
this.contextValue = `sandbox-${state}`;
5154

@@ -55,6 +58,12 @@ export class SandboxTreeItem extends vscode.TreeItem {
5558
if (sandbox.createdAt) lines.push(`Created: ${sandbox.createdAt}`);
5659
if (sandbox.eol) lines.push(`EOL: ${sandbox.eol}`);
5760
this.tooltip = new vscode.MarkdownString(lines.join('\n\n'));
61+
62+
this.command = {
63+
command: 'b2c-dx.sandbox.viewDetails',
64+
title: 'View Details',
65+
arguments: [this],
66+
};
5867
}
5968
}
6069

@@ -66,6 +75,18 @@ function getPollIntervalMs(): number {
6675
return seconds * 1000;
6776
}
6877

78+
function getSandboxDisplayName(sandbox: SandboxInfo): string {
79+
return sandbox.instance ? `${sandbox.realm ?? ''}${sandbox.realm ? '-' : ''}${sandbox.instance}` : sandbox.id;
80+
}
81+
82+
function sortSandboxesByName(sandboxes: SandboxInfo[]): SandboxInfo[] {
83+
return [...sandboxes].sort((a, b) => {
84+
const nameA = getSandboxDisplayName(a).toLowerCase();
85+
const nameB = getSandboxDisplayName(b).toLowerCase();
86+
return nameA.localeCompare(nameB);
87+
});
88+
}
89+
6990
export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxTreeNode> {
7091
private _onDidChangeTreeData = new vscode.EventEmitter<SandboxTreeNode | undefined | void>();
7192
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
@@ -167,7 +188,7 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
167188
private async getRealmChildren(element: RealmTreeItem): Promise<SandboxTreeItem[]> {
168189
const cached = this.configProvider.getCachedSandboxes(element.realm);
169190
if (cached) {
170-
return cached.map((s) => new SandboxTreeItem(s, element.realm));
191+
return sortSandboxesByName(cached).map((s) => new SandboxTreeItem(s, element.realm));
171192
}
172193

173194
const configProvider = this.configProvider.getConfigProvider();
@@ -198,7 +219,7 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
198219
},
199220
);
200221
this.configProvider.setCachedSandboxes(element.realm, sandboxes);
201-
return sandboxes.map((s) => new SandboxTreeItem(s, element.realm));
222+
return sortSandboxesByName(sandboxes).map((s) => new SandboxTreeItem(s, element.realm));
202223
} catch (err) {
203224
const message = err instanceof Error ? err.message : String(err);
204225
vscode.window.showErrorMessage(`Sandboxes (${element.realm}): ${message}`);

0 commit comments

Comments
 (0)