|
| 1 | +import {ux} from '@oclif/core'; |
| 2 | +import cliui from 'cliui'; |
1 | 3 | import {InstanceCommand} from '@salesforce/b2c-tooling/cli'; |
| 4 | +import type {OcapiComponents} from '@salesforce/b2c-tooling'; |
2 | 5 | import {t} from '../../i18n/index.js'; |
3 | 6 |
|
4 | | -interface SitesResponse { |
5 | | - _v: string; |
6 | | - count: number; |
7 | | - data: Array<{ |
8 | | - id: string; |
9 | | - display_name?: {default?: string}; |
10 | | - status?: string; |
11 | | - }>; |
12 | | - total: number; |
13 | | -} |
| 7 | +type Sites = OcapiComponents['schemas']['sites']; |
| 8 | +type Site = OcapiComponents['schemas']['site']; |
14 | 9 |
|
15 | 10 | export default class SitesList extends InstanceCommand<typeof SitesList> { |
16 | 11 | static description = t('commands.sites.list.description', 'List sites on a B2C Commerce instance'); |
17 | 12 |
|
| 13 | + static enableJsonFlag = true; |
| 14 | + |
18 | 15 | static examples = [ |
19 | 16 | '<%= config.bin %> <%= command.id %>', |
20 | 17 | '<%= config.bin %> <%= command.id %> --server my-sandbox.demandware.net', |
| 18 | + '<%= config.bin %> <%= command.id %> --json', |
21 | 19 | ]; |
22 | 20 |
|
23 | | - async run(): Promise<void> { |
24 | | - this.requireServer(); |
| 21 | + async run(): Promise<Sites> { |
25 | 22 | this.requireOAuthCredentials(); |
26 | 23 |
|
27 | | - const instance = this.createApiInstance(); |
28 | 24 | const hostname = this.resolvedConfig.hostname!; |
29 | 25 |
|
30 | 26 | this.log(t('commands.sites.list.fetching', 'Fetching sites from {{hostname}}...', {hostname})); |
31 | 27 |
|
32 | | - try { |
33 | | - const response = await instance.ocapiDataRequest('sites?select=(**)'); |
34 | | - |
35 | | - if (!response.ok) { |
36 | | - const errorText = await response.text(); |
37 | | - this.error( |
38 | | - t('commands.sites.list.fetchFailed', 'Failed to fetch sites: {{status}} {{statusText}}\n{{error}}', { |
39 | | - status: response.status, |
40 | | - statusText: response.statusText, |
41 | | - error: errorText, |
42 | | - }), |
43 | | - ); |
44 | | - } |
45 | | - |
46 | | - const data = (await response.json()) as SitesResponse; |
47 | | - |
48 | | - if (data.count === 0) { |
49 | | - this.log(t('commands.sites.list.noSites', 'No sites found.')); |
50 | | - return; |
51 | | - } |
52 | | - |
53 | | - this.log(''); |
54 | | - this.log(t('commands.sites.list.foundSites', 'Found {{count}} site(s):', {count: data.count})); |
55 | | - this.log(''); |
56 | | - |
57 | | - for (const site of data.data) { |
58 | | - const displayName = site.display_name?.default || site.id; |
59 | | - const status = site.status || 'unknown'; |
60 | | - this.log(` ${site.id}`); |
61 | | - this.log(` ${t('commands.sites.list.displayName', 'Display Name: {{name}}', {name: displayName})}`); |
62 | | - this.log(` ${t('commands.sites.list.status', 'Status: {{status}}', {status})}`); |
63 | | - this.log(''); |
64 | | - } |
65 | | - } catch (error) { |
66 | | - if (error instanceof Error) { |
67 | | - this.error(t('commands.sites.list.error', 'Failed to fetch sites: {{message}}', {message: error.message})); |
68 | | - } |
69 | | - throw error; |
| 28 | + // eslint-disable-next-line new-cap |
| 29 | + const {data, error} = await this.instance.ocapi.GET('/sites', { |
| 30 | + params: {query: {select: '(**)'}}, |
| 31 | + }); |
| 32 | + |
| 33 | + if (error) { |
| 34 | + this.error(t('commands.sites.list.error', 'Failed to fetch sites: {{message}}', {message: String(error)})); |
| 35 | + } |
| 36 | + |
| 37 | + const sites = data as Sites; |
| 38 | + |
| 39 | + // In JSON mode, just return the data - oclif handles output to stdout |
| 40 | + if (this.jsonEnabled()) { |
| 41 | + return sites; |
| 42 | + } |
| 43 | + |
| 44 | + // Human-readable table output to stdout |
| 45 | + if (!sites || sites.count === 0) { |
| 46 | + ux.stdout(t('commands.sites.list.noSites', 'No sites found.')); |
| 47 | + return sites; |
| 48 | + } |
| 49 | + |
| 50 | + this.printSitesTable(sites.data ?? []); |
| 51 | + |
| 52 | + return sites; |
| 53 | + } |
| 54 | + |
| 55 | + private printSitesTable(sites: Site[]): void { |
| 56 | + const ui = cliui({width: process.stdout.columns || 80}); |
| 57 | + |
| 58 | + // Header |
| 59 | + ui.div( |
| 60 | + {text: 'ID', width: 30, padding: [0, 2, 0, 0]}, |
| 61 | + {text: 'Display Name', width: 30, padding: [0, 2, 0, 0]}, |
| 62 | + {text: 'Status', padding: [0, 0, 0, 0]}, |
| 63 | + ); |
| 64 | + |
| 65 | + // Separator |
| 66 | + ui.div({text: '─'.repeat(70), padding: [0, 0, 0, 0]}); |
| 67 | + |
| 68 | + // Rows |
| 69 | + for (const site of sites) { |
| 70 | + const displayName = site.display_name?.default || site.id || ''; |
| 71 | + const status = site.storefront_status || 'unknown'; |
| 72 | + |
| 73 | + ui.div( |
| 74 | + {text: site.id || '', width: 30, padding: [0, 2, 0, 0]}, |
| 75 | + {text: displayName, width: 30, padding: [0, 2, 0, 0]}, |
| 76 | + {text: status, padding: [0, 0, 0, 0]}, |
| 77 | + ); |
70 | 78 | } |
| 79 | + |
| 80 | + ux.stdout(ui.toString()); |
71 | 81 | } |
72 | 82 | } |
0 commit comments