Skip to content

Commit 1fe65b7

Browse files
committed
addressing review comments
1 parent 3b9af7d commit 1fe65b7

7 files changed

Lines changed: 96 additions & 73 deletions

File tree

packages/b2c-cli/src/commands/scapi/replications/list.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const COLUMNS: Record<string, ColumnDef<PublishProcessResponse>> = {
4141

4242
const DEFAULT_COLUMNS = ['id', 'status', 'entityType', 'entityId', 'startTime'];
4343

44+
const tableRenderer = new TableRenderer(COLUMNS);
45+
4446
export default class ReplicationsList extends GranularReplicationsCommand<typeof ReplicationsList> {
4547
static description = withDocs(
4648
t('commands.replications.list.description', 'List granular replication processes'),
@@ -99,7 +101,6 @@ export default class ReplicationsList extends GranularReplicationsCommand<typeof
99101

100102
const processes = result.data.data || [];
101103
const columns = this.getSelectedColumns();
102-
const tableRenderer = new TableRenderer(COLUMNS);
103104
ux.stdout('\n');
104105
tableRenderer.render(processes, columns);
105106

@@ -116,8 +117,13 @@ export default class ReplicationsList extends GranularReplicationsCommand<typeof
116117
const extended = this.flags.extended;
117118

118119
if (columnsFlag) {
119-
// User specified explicit columns
120-
return columnsFlag.split(',').map((c) => c.trim());
120+
const requested = columnsFlag.split(',').map((c) => c.trim());
121+
const valid = tableRenderer.validateColumnKeys(requested);
122+
if (valid.length === 0) {
123+
this.warn(`No valid columns specified. Available: ${tableRenderer.getColumnKeys().join(', ')}`);
124+
return DEFAULT_COLUMNS;
125+
}
126+
return valid;
121127
}
122128

123129
if (extended) {

packages/b2c-cli/src/utils/ecdn/base-command.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
import {Command} from '@oclif/core';
77
import {OAuthCommand} from '@salesforce/b2c-tooling-sdk/cli';
8-
import {createCdnZonesClient, toOrganizationId, type CdnZonesClient} from '@salesforce/b2c-tooling-sdk/clients';
8+
import {createCdnZonesClient, type CdnZonesClient} from '@salesforce/b2c-tooling-sdk/clients';
99
import {t} from '../../i18n/index.js';
1010

1111
/**
@@ -82,21 +82,4 @@ export abstract class EcdnCommand<T extends typeof Command> extends OAuthCommand
8282
}
8383
return this._cdnZonesRwClient;
8484
}
85-
86-
/**
87-
* Get the organization ID from resolved config.
88-
* @throws Error if tenant ID is not provided through any source
89-
*/
90-
protected getOrganizationId(): string {
91-
const {tenantId} = this.resolvedConfig.values;
92-
if (!tenantId) {
93-
this.error(
94-
t(
95-
'error.tenantIdRequired',
96-
'tenant-id is required. Provide via --tenant-id flag, SFCC_TENANT_ID env var, or tenant-id in dw.json.',
97-
),
98-
);
99-
}
100-
return toOrganizationId(tenantId);
101-
}
10285
}

packages/b2c-cli/src/utils/scapi/replications.ts

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55
*/
66
import {Command} from '@oclif/core';
77
import {OAuthCommand} from '@salesforce/b2c-tooling-sdk/cli';
8-
import {
9-
createGranularReplicationsClient,
10-
type GranularReplicationsClient,
11-
toOrganizationId,
12-
} from '@salesforce/b2c-tooling-sdk';
8+
import {createGranularReplicationsClient, type GranularReplicationsClient} from '@salesforce/b2c-tooling-sdk';
139
import {t} from '../../i18n/index.js';
1410

1511
/**
@@ -24,17 +20,22 @@ export abstract class GranularReplicationsCommand<T extends typeof Command> exte
2420
* Gets or creates a Granular Replications API client.
2521
*
2622
* Requires:
27-
* - shortCode configuration (via b2c config:set --short-code <code>)
23+
* - short code (--short-code, SFCC_SHORTCODE, or dw.json)
2824
* - OAuth credentials
29-
* - tenantId (from --tenant-id flag or config)
25+
* - tenant ID (--tenant-id, SFCC_TENANT_ID, or dw.json)
3026
*/
3127
protected get granularReplicationsClient(): GranularReplicationsClient {
3228
if (!this._granularReplicationsClient) {
3329
const shortCode = this.resolvedConfig.values.shortCode;
34-
const tenantId = this.getTenantId();
30+
const tenantId = this.requireTenantId();
3531

3632
if (!shortCode) {
37-
this.error('shortCode configuration is required. Run: b2c config:set --short-code <code>');
33+
this.error(
34+
t(
35+
'error.shortCodeRequired',
36+
'SCAPI short code required. Provide --short-code, set SFCC_SHORTCODE, or configure short-code in dw.json.',
37+
),
38+
);
3839
}
3940

4041
this._granularReplicationsClient = createGranularReplicationsClient(
@@ -44,28 +45,4 @@ export abstract class GranularReplicationsCommand<T extends typeof Command> exte
4445
}
4546
return this._granularReplicationsClient;
4647
}
47-
48-
/**
49-
* Get the organization ID (with f_ecom_ prefix) for API path parameters.
50-
*/
51-
protected getOrganizationId(): string {
52-
return toOrganizationId(this.getTenantId());
53-
}
54-
55-
/**
56-
* Get the tenant ID from resolved config.
57-
* @throws Error if tenant ID is not provided through any source
58-
*/
59-
protected getTenantId(): string {
60-
const {tenantId} = this.resolvedConfig.values;
61-
if (!tenantId) {
62-
this.error(
63-
t(
64-
'error.tenantIdRequired',
65-
'tenant-id is required. Provide via --tenant-id flag, SFCC_TENANT_ID env var, or tenant-id in dw.json.',
66-
),
67-
);
68-
}
69-
return tenantId;
70-
}
7148
}

packages/b2c-cli/src/utils/scapi/schemas.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {OAuthCommand} from '@salesforce/b2c-tooling-sdk/cli';
88
import {
99
createScapiSchemasClient,
1010
getApiErrorMessage,
11-
toOrganizationId,
1211
type ScapiSchemasClient,
1312
} from '@salesforce/b2c-tooling-sdk/clients';
1413
import {t} from '../../i18n/index.js';
@@ -25,23 +24,6 @@ export function formatApiError(error: unknown, response: Response): string {
2524
* Provides common flags and helper methods for interacting with the SCAPI Schemas API.
2625
*/
2726
export abstract class ScapiSchemasCommand<T extends typeof Command> extends OAuthCommand<T> {
28-
/**
29-
* Get the organization ID from resolved config.
30-
* @throws Error if tenant ID is not provided through any source
31-
*/
32-
protected getOrganizationId(): string {
33-
const {tenantId} = this.resolvedConfig.values;
34-
if (!tenantId) {
35-
this.error(
36-
t(
37-
'error.tenantIdRequired',
38-
'tenant-id is required. Provide via --tenant-id flag, SFCC_TENANT_ID env var, or tenant-id in dw.json.',
39-
),
40-
);
41-
}
42-
return toOrganizationId(tenantId);
43-
}
44-
4527
/**
4628
* Get the SCAPI Schemas client, ensuring short code and tenant ID are configured.
4729
*/

packages/b2c-cli/test/commands/scapi/replications/list.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,44 @@ describe('scapi replications list', () => {
150150
}
151151
});
152152
});
153+
154+
describe('getSelectedColumns', () => {
155+
let config: Config;
156+
157+
beforeEach(async () => {
158+
config = await Config.load();
159+
});
160+
161+
afterEach(() => {
162+
sinon.restore();
163+
});
164+
165+
it('returns default columns when no flags provided', async () => {
166+
const command: any = new ReplicationsList([], config);
167+
stubParse(command, {}, {});
168+
await command.init();
169+
170+
expect(command.getSelectedColumns()).to.deep.equal(['id', 'status', 'entityType', 'entityId', 'startTime']);
171+
});
172+
173+
it('filters invalid column names and warns when none valid', async () => {
174+
const command: any = new ReplicationsList([], config);
175+
stubParse(command, {columns: 'nope,bad'}, {});
176+
await command.init();
177+
178+
const warn = sinon.stub(command, 'warn').returns(void 0);
179+
const columns = command.getSelectedColumns();
180+
181+
expect(columns).to.deep.equal(['id', 'status', 'entityType', 'entityId', 'startTime']);
182+
expect(warn.calledOnce).to.equal(true);
183+
});
184+
185+
it('returns only valid columns from --columns', async () => {
186+
const command: any = new ReplicationsList([], config);
187+
stubParse(command, {columns: 'id,status,bogus'}, {});
188+
await command.init();
189+
190+
expect(command.getSelectedColumns()).to.deep.equal(['id', 'status']);
191+
});
192+
});
153193
});

packages/b2c-tooling-sdk/src/cli/oauth-command.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {OAuthStrategy} from '../auth/oauth.js';
1212
import {ImplicitOAuthStrategy} from '../auth/oauth-implicit.js';
1313
import {t} from '../i18n/index.js';
1414
import {DEFAULT_ACCOUNT_MANAGER_HOST} from '../defaults.js';
15-
import {normalizeTenantId} from '../clients/custom-apis.js';
15+
import {normalizeTenantId, toOrganizationId} from '../clients/custom-apis.js';
1616

1717
/**
1818
* Default OAuth authentication methods array used by getOAuthStrategy.
@@ -225,4 +225,11 @@ export abstract class OAuthCommand<T extends typeof Command> extends BaseCommand
225225
}
226226
return normalizeTenantId(tenantId);
227227
}
228+
229+
/**
230+
* Organization ID (`f_ecom_*`) for API path parameters from the resolved tenant.
231+
*/
232+
protected getOrganizationId(): string {
233+
return toOrganizationId(this.requireTenantId());
234+
}
228235
}

packages/b2c-tooling-sdk/test/cli/oauth-command.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class TestOAuthCommand extends OAuthCommand<typeof TestOAuthCommand> {
3737
public testRequireTenantId() {
3838
return this.requireTenantId();
3939
}
40+
41+
public testGetOrganizationId() {
42+
return this.getOrganizationId();
43+
}
4044
}
4145

4246
// Test command with default client ID (simulates AmCommand/OdsCommand behavior)
@@ -166,6 +170,30 @@ describe('cli/oauth-command', () => {
166170
});
167171
});
168172

173+
describe('getOrganizationId', () => {
174+
it('returns f_ecom-prefixed org ID from tenant', async () => {
175+
stubParse(command, {'client-id': 'test-client', 'tenant-id': 'zzxy_prd'});
176+
await command.init();
177+
178+
expect(command.testGetOrganizationId()).to.equal('f_ecom_zzxy_prd');
179+
});
180+
181+
it('throws when tenant ID missing', async () => {
182+
stubParse(command, {'client-id': 'test-client'});
183+
await command.init();
184+
185+
const errorStub = sinon.stub(command, 'error').throws(new Error('Expected error'));
186+
187+
try {
188+
command.testGetOrganizationId();
189+
} catch {
190+
// Expected
191+
}
192+
193+
expect(errorStub.called).to.be.true;
194+
});
195+
});
196+
169197
describe('getDefaultClientId', () => {
170198
it('returns undefined by default (no fallback)', async () => {
171199
stubParse(command);

0 commit comments

Comments
 (0)