Skip to content

Commit a80089d

Browse files
Merge pull request #1079 from salesforcecli/wr/cacheMiss
Wr/cache miss
2 parents 3007e19 + f559dea commit a80089d

File tree

4 files changed

+93
-19
lines changed

4 files changed

+93
-19
lines changed

messages/resume_scratch.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Use the job ID of the most recent incomplete scratch org.
3636

3737
There are no recent job IDs (ScratchOrgInfo requests) in your cache. Maybe it completed or already resumed?
3838

39+
# error.jobIdMismatch
40+
41+
There are no recent job IDs (ScratchOrgInfo requests) in your cache that match %s. Maybe it completed?
42+
3943
# success
4044

4145
Your scratch org is ready.

src/commands/org/create/scratch.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,25 @@
66
*/
77

88
import {
9-
Messages,
109
Lifecycle,
11-
ScratchOrgLifecycleEvent,
12-
scratchOrgLifecycleEventName,
10+
Messages,
1311
Org,
1412
scratchOrgCreate,
13+
ScratchOrgLifecycleEvent,
14+
scratchOrgLifecycleEventName,
1515
SfError,
1616
} from '@salesforce/core';
17-
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
17+
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
1818
import { Duration } from '@salesforce/kit';
1919
import { buildScratchOrgRequest } from '../../../shared/scratchOrgRequest.js';
2020
import { buildStatus } from '../../../shared/scratchOrgOutput.js';
2121
import { ScratchCreateResponse } from '../../../shared/orgTypes.js';
22+
2223
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2324
const messages = Messages.loadMessages('@salesforce/plugin-org', 'create_scratch');
2425

25-
export const secretTimeout = 60_000;
26-
2726
const definitionFileHelpGroupName = 'Definition File Override';
28-
export default class EnvCreateScratch extends SfCommand<ScratchCreateResponse> {
27+
export default class OrgCreateScratch extends SfCommand<ScratchCreateResponse> {
2928
public static readonly summary = messages.getMessage('summary');
3029
public static readonly description = messages.getMessage('description');
3130
public static readonly examples = messages.getMessages('examples');
@@ -158,7 +157,7 @@ export default class EnvCreateScratch extends SfCommand<ScratchCreateResponse> {
158157

159158
public async run(): Promise<ScratchCreateResponse> {
160159
const lifecycle = Lifecycle.getInstance();
161-
const { flags } = await this.parse(EnvCreateScratch);
160+
const { flags } = await this.parse(OrgCreateScratch);
162161
const baseUrl = flags['target-dev-hub'].getField(Org.Fields.INSTANCE_URL)?.toString();
163162
if (!baseUrl) {
164163
throw new SfError('No instance URL found for the dev hub');

src/commands/org/resume/scratch.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,23 @@
77

88
import { strict as assert } from 'node:assert';
99

10-
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
10+
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
1111
import {
12+
Lifecycle,
1213
Messages,
13-
scratchOrgResume,
1414
ScratchOrgCache,
15-
Lifecycle,
1615
ScratchOrgLifecycleEvent,
1716
scratchOrgLifecycleEventName,
17+
scratchOrgResume,
18+
SfError,
1819
} from '@salesforce/core';
1920
import { ScratchCreateResponse } from '../../../shared/orgTypes.js';
2021
import { buildStatus } from '../../../shared/scratchOrgOutput.js';
2122

2223
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2324
const messages = Messages.loadMessages('@salesforce/plugin-org', 'resume_scratch');
2425

25-
export default class EnvResumeScratch extends SfCommand<ScratchCreateResponse> {
26+
export default class OrgResumeScratch extends SfCommand<ScratchCreateResponse> {
2627
public static readonly summary = messages.getMessage('summary');
2728
public static readonly description = messages.getMessage('description');
2829
public static readonly examples = messages.getMessages('examples');
@@ -45,7 +46,7 @@ export default class EnvResumeScratch extends SfCommand<ScratchCreateResponse> {
4546
};
4647

4748
public async run(): Promise<ScratchCreateResponse> {
48-
const { flags } = await this.parse(EnvResumeScratch);
49+
const { flags } = await this.parse(OrgResumeScratch);
4950
const cache = await ScratchOrgCache.create();
5051
const lifecycle = Lifecycle.getInstance();
5152

@@ -54,7 +55,7 @@ export default class EnvResumeScratch extends SfCommand<ScratchCreateResponse> {
5455

5556
// oclif doesn't know that the exactlyOne flag will ensure that one of these is set, and there we definitely have a jobID.
5657
assert(jobId);
57-
const { hubBaseUrl } = cache.get(jobId);
58+
const hubBaseUrl = cache.get(jobId)?.hubBaseUrl;
5859
let lastStatus: string | undefined;
5960

6061
lifecycle.on<ScratchOrgLifecycleEvent>(scratchOrgLifecycleEventName, async (data): Promise<void> => {
@@ -66,11 +67,20 @@ export default class EnvResumeScratch extends SfCommand<ScratchCreateResponse> {
6667
this.log();
6768
this.spinner.start('Creating Scratch Org');
6869

69-
const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgResume(jobId);
70-
this.spinner.stop(lastStatus);
70+
try {
71+
const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgResume(jobId);
72+
this.spinner.stop(lastStatus);
7173

72-
this.log();
73-
this.logSuccess(messages.getMessage('success'));
74-
return { username, scratchOrgInfo, authFields, warnings, orgId: authFields?.orgId };
74+
this.log();
75+
this.logSuccess(messages.getMessage('success'));
76+
return { username, scratchOrgInfo, authFields, warnings, orgId: authFields?.orgId };
77+
} catch (e) {
78+
if (cache.keys() && e instanceof Error && e.name === 'CacheMissError') {
79+
// we have something in the cache, but it didn't match what the user passed in
80+
throw messages.createError('error.jobIdMismatch', [jobId]);
81+
} else {
82+
throw SfError.wrap(e);
83+
}
84+
}
7585
}
7686
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2023, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import { config, expect } from 'chai';
8+
import sinon from 'sinon';
9+
import { stubMethod } from '@salesforce/ts-sinon';
10+
import { Messages, ScratchOrgCache, SfError } from '@salesforce/core';
11+
12+
import { stubSfCommandUx, stubUx } from '@salesforce/sf-plugins-core';
13+
import OrgResumeScratch from '../../../src/commands/org/resume/scratch.js';
14+
15+
config.truncateThreshold = 0;
16+
17+
describe('org:resume:scratch', () => {
18+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
19+
const messages = Messages.loadMessages('@salesforce/plugin-org', 'resume_scratch');
20+
const sandbox = sinon.createSandbox();
21+
beforeEach(() => {
22+
stubSfCommandUx(sandbox);
23+
stubUx(sandbox);
24+
});
25+
26+
afterEach(() => {
27+
sandbox.restore();
28+
});
29+
30+
it('will handle a cache miss gracefully', async () => {
31+
stubMethod(sandbox, ScratchOrgCache, 'create').resolves({
32+
get: () => {},
33+
keys: () => {},
34+
});
35+
36+
try {
37+
await OrgResumeScratch.run(['--job-id', '2SRFOOFOOFOOFOOFOO']);
38+
expect(false, 'ResumeSandbox should have thrown sandboxCreateNotComplete');
39+
} catch (err: unknown) {
40+
const error = err as SfError;
41+
expect(error.message).to.equal('The ScratchOrgInfoId 2SRFOOFOOFOOFOOFOO was not found in the cache.');
42+
expect(error.name).to.equal('CacheMissError');
43+
}
44+
});
45+
46+
it('will handle a cache miss gracefully and change message when other keys exist', async () => {
47+
stubMethod(sandbox, ScratchOrgCache, 'create').resolves({
48+
get: () => {},
49+
keys: () => ['abc', '123'],
50+
});
51+
52+
try {
53+
await OrgResumeScratch.run(['--job-id', '2SRFOOFOOFOOFOOFOO']);
54+
expect(false, 'ResumeSandbox should have thrown sandboxCreateNotComplete');
55+
} catch (err: unknown) {
56+
const error = err as SfError;
57+
expect(error.message).to.equal(messages.getMessage('error.jobIdMismatch', ['2SRFOOFOOFOOFOOFOO']));
58+
expect(error.name).to.equal('JobIdMismatchError');
59+
}
60+
});
61+
});

0 commit comments

Comments
 (0)