Skip to content

Commit 6b72da4

Browse files
authored
fix: make OAuth credentials conditional in code commands (#74)
* fix: make OAuth credentials conditional in code commands OAuth is now only required for code deploy/watch when actually needed: - No code version specified (auto-discovery via OCAPI) - --reload flag is set (reload via OCAPI) When using basic auth with an explicit --code-version, OAuth is not required. Error messages now explain why OAuth is needed and link to configuration docs. Closes #35 * test: add coverage for OAuth credential check in code deploy Test both error paths when OAuth credentials are missing: - No code version specified (needs auto-discovery) - --reload flag set (needs OCAPI call)
1 parent 20e2512 commit 6b72da4

3 files changed

Lines changed: 81 additions & 4 deletions

File tree

packages/b2c-cli/src/commands/code/deploy.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,33 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
5959

6060
async run(): Promise<DeployResult> {
6161
this.requireWebDavCredentials();
62-
this.requireOAuthCredentials();
6362

6463
const hostname = this.resolvedConfig.values.hostname!;
6564
let version = this.resolvedConfig.values.codeVersion;
6665

66+
// OAuth is only required if:
67+
// 1. No code version specified (need to auto-discover via OCAPI)
68+
// 2. --reload flag is set (need to call OCAPI to reload)
69+
const needsOAuth = !version || this.flags.reload;
70+
if (needsOAuth && !this.hasOAuthCredentials()) {
71+
const reason = version
72+
? t(
73+
'commands.code.deploy.oauthRequiredForReload',
74+
'The --reload flag requires OAuth credentials to reload the code version via OCAPI.',
75+
)
76+
: t(
77+
'commands.code.deploy.oauthRequiredForDiscovery',
78+
'No code version specified. OAuth credentials are required to auto-discover the active code version.',
79+
);
80+
this.error(
81+
t(
82+
'commands.code.deploy.oauthRequired',
83+
'{{reason}}\n\nProvide --code-version to use basic auth only, or configure OAuth credentials.\nSee: https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/configuration.html',
84+
{reason},
85+
),
86+
);
87+
}
88+
6789
// If no code version specified, discover the active one
6890
if (!version) {
6991
this.warn(

packages/b2c-cli/src/commands/code/watch.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,20 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
3636

3737
async run(): Promise<void> {
3838
this.requireWebDavCredentials();
39-
this.requireOAuthCredentials();
4039

4140
const hostname = this.resolvedConfig.values.hostname!;
4241
const version = this.resolvedConfig.values.codeVersion;
4342

43+
// OAuth is only required if no code version specified (need to auto-discover via OCAPI)
44+
if (!version && !this.hasOAuthCredentials()) {
45+
this.error(
46+
t(
47+
'commands.code.watch.oauthRequired',
48+
'No code version specified. OAuth credentials are required to auto-discover the active code version.\n\nProvide --code-version to use basic auth only, or configure OAuth credentials.\nSee: https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/configuration.html',
49+
),
50+
);
51+
}
52+
4453
this.log(t('commands.code.watch.starting', 'Starting watcher for {{path}}', {path: this.cartridgePath}));
4554
this.log(t('commands.code.watch.target', 'Target: {{hostname}}', {hostname}));
4655
if (version) {

packages/b2c-cli/test/commands/code/deploy.test.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('code deploy', () => {
2424
function stubCommon(command: any) {
2525
const instance = {config: {hostname: 'example.com', codeVersion: 'v1'}};
2626
sinon.stub(command, 'requireWebDavCredentials').returns(void 0);
27-
sinon.stub(command, 'requireOAuthCredentials').returns(void 0);
27+
sinon.stub(command, 'hasOAuthCredentials').returns(true);
2828
sinon.stub(command, 'log').returns(void 0);
2929
sinon.stub(command, 'warn').returns(void 0);
3030
sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: 'v1'}}));
@@ -113,11 +113,57 @@ describe('code deploy', () => {
113113
expect(result.reloaded).to.equal(false);
114114
});
115115

116+
it('errors when no code version and no OAuth credentials', async () => {
117+
const command: any = await createCommand({}, {cartridgePath: '.'});
118+
119+
sinon.stub(command, 'requireWebDavCredentials').returns(void 0);
120+
sinon.stub(command, 'hasOAuthCredentials').returns(false);
121+
sinon.stub(command, 'log').returns(void 0);
122+
sinon.stub(command, 'warn').returns(void 0);
123+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: undefined}}));
124+
125+
const errorStub = sinon.stub(command, 'error').throws(new Error('OAuth required'));
126+
127+
try {
128+
await command.run();
129+
expect.fail('Should have thrown');
130+
} catch {
131+
// expected
132+
}
133+
134+
expect(errorStub.calledOnce).to.equal(true);
135+
const errorMessage = errorStub.firstCall.args[0];
136+
expect(errorMessage).to.include('auto-discover');
137+
});
138+
139+
it('errors when --reload flag set but no OAuth credentials', async () => {
140+
const command: any = await createCommand({reload: true}, {cartridgePath: '.'});
141+
142+
sinon.stub(command, 'requireWebDavCredentials').returns(void 0);
143+
sinon.stub(command, 'hasOAuthCredentials').returns(false);
144+
sinon.stub(command, 'log').returns(void 0);
145+
sinon.stub(command, 'warn').returns(void 0);
146+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: 'v1'}}));
147+
148+
const errorStub = sinon.stub(command, 'error').throws(new Error('OAuth required'));
149+
150+
try {
151+
await command.run();
152+
expect.fail('Should have thrown');
153+
} catch {
154+
// expected
155+
}
156+
157+
expect(errorStub.calledOnce).to.equal(true);
158+
const errorMessage = errorStub.firstCall.args[0];
159+
expect(errorMessage).to.include('reload');
160+
});
161+
116162
it('uses active code version when resolvedConfig is missing codeVersion', async () => {
117163
const command: any = await createCommand({}, {cartridgePath: '.'});
118164

119165
sinon.stub(command, 'requireWebDavCredentials').returns(void 0);
120-
sinon.stub(command, 'requireOAuthCredentials').returns(void 0);
166+
sinon.stub(command, 'hasOAuthCredentials').returns(true);
121167
sinon.stub(command, 'log').returns(void 0);
122168
sinon.stub(command, 'warn').returns(void 0);
123169

0 commit comments

Comments
 (0)