Skip to content

Commit

Permalink
fix: check devhubs and non-scratch orgs and sandboxes
Browse files Browse the repository at this point in the history
  • Loading branch information
shetzel committed Aug 1, 2023
1 parent b3a6b76 commit 57c0bae
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 48 deletions.
116 changes: 72 additions & 44 deletions src/org/authInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,21 +392,29 @@ export class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
// authInfo before it is necessary.
const logger = await Logger.child('Common', { tag: 'identifyPossibleScratchOrgs' });

// return if we already know the hub org we know it is a devhub or prod-like or no orgId present
// return if we already know the hub org, we know it is a devhub or prod-like, or no orgId present
if (fields.isDevHub || fields.devHubUsername || !fields.orgId) return;

logger.debug('getting devHubs to identify scratch orgs and sandboxes');
logger.debug('getting devHubs and prod orgs to identify scratch orgs and sandboxes');

// TODO: return if url is not sandbox-like to avoid constantly asking about production orgs
// TODO: someday we make this easier by asking the org if it is a scratch org

const hubAuthInfos = await AuthInfo.getDevHubAuthInfos();
// Get a list of org auths that are known not to be devhubs, scratch orgs, or sandboxes.
const possibleProdOrgs = await AuthInfo.listAllAuthorizations(
(orgAuth) => orgAuth && !orgAuth.isDevHub && !orgAuth.isScratchOrg && !orgAuth.isSandbox
);

logger.debug(`found ${hubAuthInfos.length} DevHubs`);
if (hubAuthInfos.length === 0) return;
logger.debug(`found ${possibleProdOrgs.length} possible prod orgs`);
if (hubAuthInfos.length === 0 && possibleProdOrgs.length === 0) {
return;
}

// ask all those orgs if they know this orgId
await Promise.all(
hubAuthInfos.map(async (hubAuthInfo) => {
await Promise.all([
...hubAuthInfos.map(async (hubAuthInfo) => {
try {
const soi = await AuthInfo.queryScratchOrg(hubAuthInfo.username, fields.orgId as string);
// if any return a result
Expand All @@ -429,49 +437,16 @@ export class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
} catch (error) {
if (error instanceof Error && error.name === 'SingleRecordQuery_NoRecords' && fields.orgId) {
// Not a scratch org owned by this hub. Check if it's a sandbox.
try {
const prodOrg = await Org.create({ aliasOrUsername: hubAuthInfo.username });
const sbxProcess = await prodOrg.querySandboxProcessByOrgId(fields.orgId);
logger.debug(`${fields.orgId} is a sandbox of ${hubAuthInfo.username}`);

try {
await orgAuthInfo.save({
...fields,
isScratch: false,
isSandbox: true,
});
} catch (err) {
logger.debug(`error updating auth file for: ${orgAuthInfo.getUsername()}`, err);
}

try {
// set the sandbox config value
const sfSandbox = {
sandboxUsername: fields.username,
sandboxOrgId: fields.orgId,
prodOrgUsername: hubAuthInfo.username,
sandboxName: sbxProcess.SandboxName,
sandboxProcessId: sbxProcess.Id,
sandboxInfoId: sbxProcess.SandboxInfoId,
timestamp: new Date().toISOString(),
} as SandboxFields;

const stateAggregator = await StateAggregator.getInstance();
stateAggregator.sandboxes.set(fields.orgId, sfSandbox);
logger.debug(`writing sandbox auth file for: ${orgAuthInfo.getUsername()} with ID: ${fields.orgId}`);
await stateAggregator.sandboxes.write(fields.orgId);
} catch (e) {
logger.debug(`error writing sandbox auth file for: ${orgAuthInfo.getUsername()}`, e);
}
} catch (err) {
logger.debug(`${fields.orgId} is not a sandbox of ${hubAuthInfo.username}`);
}
await AuthInfo.identifyPossibleSandbox(hubAuthInfo, fields, orgAuthInfo, logger);
} else {
logger.error(`Error connecting to devhub ${hubAuthInfo.username}`, error);
}
}
})
);
}),
...possibleProdOrgs.map(async (pOrgAuthInfo) => {
await AuthInfo.identifyPossibleSandbox(pOrgAuthInfo, fields, orgAuthInfo, logger);
}),
]);
}

/**
Expand All @@ -481,6 +456,59 @@ export class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
return AuthInfo.listAllAuthorizations((possibleHub) => possibleHub?.isDevHub ?? false);
}

private static async identifyPossibleSandbox(
possibleProdOrg: OrgAuthorization,
fields: AuthFields,
orgAuthInfo: AuthInfo,
logger: Logger
): Promise<void> {
if (!fields.orgId) {
return;
}

try {
const prodOrg = await Org.create({ aliasOrUsername: possibleProdOrg.username });
const sbxProcess = await prodOrg.querySandboxProcessByOrgId(fields.orgId);
if (!sbxProcess?.SandboxInfoId) {
return;
}
logger.debug(`${fields.orgId} is a sandbox of ${possibleProdOrg.username}`);

try {
await orgAuthInfo.save({
...fields,
isScratch: false,
isSandbox: true,
});
} catch (err) {
logger.debug(`error updating auth file for: ${orgAuthInfo.getUsername()}`, err);
throw err; // rethrow; don't want a sandbox config file with an invalid auth file
}

try {
// set the sandbox config value
const sfSandbox = {
sandboxUsername: fields.username,
sandboxOrgId: fields.orgId,
prodOrgUsername: possibleProdOrg.username,
sandboxName: sbxProcess.SandboxName,
sandboxProcessId: sbxProcess.Id,
sandboxInfoId: sbxProcess.SandboxInfoId,
timestamp: new Date().toISOString(),
} as SandboxFields;

const stateAggregator = await StateAggregator.getInstance();
stateAggregator.sandboxes.set(fields.orgId, sfSandbox);
logger.debug(`writing sandbox auth file for: ${orgAuthInfo.getUsername()} with ID: ${fields.orgId}`);
await stateAggregator.sandboxes.write(fields.orgId);
} catch (e) {
logger.debug(`error writing sandbox auth file for: ${orgAuthInfo.getUsername()}`, e);
}
} catch (err) {
logger.debug(`${fields.orgId} is not a sandbox of ${possibleProdOrg.username}`);
}
}

/**
* Checks active scratch orgs to match by the ScratchOrg field (the 15-char org id)
* if you pass an 18-char scratchOrgId, it will be trimmed to 15-char for query purposes
Expand Down
63 changes: 59 additions & 4 deletions test/unit/org/authInfoTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1788,8 +1788,10 @@ describe('AuthInfo', () => {
});

const getDevHubAuthInfosStub = stubMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos').resolves([]);
stubMethod($$.SANDBOX, AuthInfo, 'listAllAuthorizations').resolves([]);
const queryScratchOrgStub = stubMethod($$.SANDBOX, AuthInfo, 'queryScratchOrg');
const authInfoSaveStub = stubMethod($$.SANDBOX, AuthInfo.prototype, 'save');
stubMethod($$.SANDBOX, Org.prototype, 'querySandboxProcessByOrgId').throws();

await AuthInfo.identifyPossibleScratchOrgs({ orgId: user1.orgId }, authInfo);
expect(getDevHubAuthInfosStub.callCount).to.be.equal(1);
Expand All @@ -1811,6 +1813,7 @@ describe('AuthInfo', () => {
const getDevHubAuthInfosStub = stubMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos').resolves([]);
const queryScratchOrgStub = stubMethod($$.SANDBOX, AuthInfo, 'queryScratchOrg');
const authInfoSaveStub = stubMethod($$.SANDBOX, AuthInfo.prototype, 'save');
stubMethod($$.SANDBOX, Org.prototype, 'querySandboxProcessByOrgId').throws();

await AuthInfo.identifyPossibleScratchOrgs(authInfo.getFields(), authInfo);
expect(getDevHubAuthInfosStub.callCount).to.be.equal(0);
Expand All @@ -1833,6 +1836,7 @@ describe('AuthInfo', () => {
const getDevHubAuthInfosSpy = spyMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos');
const queryScratchOrgStub = stubMethod($$.SANDBOX, AuthInfo, 'queryScratchOrg');
const authInfoSaveStub = stubMethod($$.SANDBOX, AuthInfo.prototype, 'save');
stubMethod($$.SANDBOX, Org.prototype, 'querySandboxProcessByOrgId').throws();

await AuthInfo.identifyPossibleScratchOrgs(authInfo.getFields(), authInfo);
expect(getDevHubAuthInfosSpy.callCount).to.be.equal(0);
Expand All @@ -1849,19 +1853,67 @@ describe('AuthInfo', () => {
username: user1.username,
});

const getDevHubAuthInfosSpy = spyMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos');
const devhubAuths = await AuthInfo.getDevHubAuthInfos();
const getDevHubAuthInfosStub = stubMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos').resolves(devhubAuths);
stubMethod($$.SANDBOX, AuthInfo, 'listAllAuthorizations').resolves([]);
const queryScratchOrgStub = stubMethod($$.SANDBOX, AuthInfo, 'queryScratchOrg').resolves({
Id: '123',
ExpirationDate: '2020-01-01',
});
const authInfoSaveStub = stubMethod($$.SANDBOX, AuthInfo.prototype, 'save');
stubMethod($$.SANDBOX, Org.prototype, 'querySandboxProcessByOrgId').throws();

await AuthInfo.identifyPossibleScratchOrgs(authInfo.getFields(), authInfo);
expect(getDevHubAuthInfosSpy.callCount).to.be.equal(1);
expect(getDevHubAuthInfosStub.callCount).to.be.equal(1);
expect(queryScratchOrgStub.callCount).to.be.equal(1);
expect(authInfoSaveStub.callCount).to.be.equal(1);
});

it('should update auth file as a sandbox', async () => {
it('should update auth file as a sandbox from devhub', async () => {
adminTestData.makeDevHub();

await $$.stubAuths(adminTestData, user1);

const authInfo = await AuthInfo.create({
username: user1.username,
});

const stateAggregator = await StateAggregator.getInstance();
const stateAggregatorStub = stubMethod($$.SANDBOX, StateAggregator, 'getInstance');
const sandboxSetStub = stubMethod($$.SANDBOX, stateAggregator.sandboxes, 'set');
const sandboxWriteStub = stubMethod($$.SANDBOX, stateAggregator.sandboxes, 'write');
stateAggregatorStub.resolves(stateAggregator);
const devhubAuths = await AuthInfo.getDevHubAuthInfos();
stubMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos').resolves(devhubAuths);
stubMethod($$.SANDBOX, AuthInfo, 'listAllAuthorizations').resolves([]);
const authInfoSaveStub = stubMethod($$.SANDBOX, AuthInfo.prototype, 'save').resolves();
const queryScratchOrgStub = stubMethod($$.SANDBOX, AuthInfo, 'queryScratchOrg');
const queryScratchOrgError = new Error('not a scratch org');
queryScratchOrgError.name = 'SingleRecordQuery_NoRecords';
queryScratchOrgStub.throws(queryScratchOrgError);
const sbxQueryStub = stubMethod($$.SANDBOX, Org.prototype, 'querySandboxProcessByOrgId');
sbxQueryStub.resolves({
Id: '0GRB0000000L0ZVOA0',
Status: 'Completed',
SandboxName: 'TestSandbox',
SandboxInfoId: '0GQB0000000PCOdOAO',
LicenseType: 'DEVELOPER',
CreatedDate: '2021-01-22T22:49:52.000+0000',
});

await AuthInfo.identifyPossibleScratchOrgs(authInfo.getFields(), authInfo);

expect(authInfoSaveStub.callCount).to.be.equal(2);
expect(authInfoSaveStub.secondCall.args[0]).to.have.property('isSandbox', true);
expect(authInfoSaveStub.secondCall.args[0]).to.have.property('isScratch', false);
expect(sandboxSetStub.calledOnce).to.be.true;
expect(sandboxSetStub.firstCall.args[0]).to.equal(authInfo.getFields().orgId);
expect(sandboxSetStub.firstCall.args[1]).to.have.property('prodOrgUsername', adminTestData.username);
expect(sandboxWriteStub.calledOnce).to.be.true;
expect(sandboxWriteStub.firstCall.args[0]).to.equal(authInfo.getFields().orgId);
});

it('should update auth file as a sandbox from possible prod orgs', async () => {
adminTestData.makeDevHub();

await $$.stubAuths(adminTestData, user1);
Expand All @@ -1875,6 +1927,9 @@ describe('AuthInfo', () => {
const sandboxSetStub = stubMethod($$.SANDBOX, stateAggregator.sandboxes, 'set');
const sandboxWriteStub = stubMethod($$.SANDBOX, stateAggregator.sandboxes, 'write');
stateAggregatorStub.resolves(stateAggregator);
const devhubAuths = await AuthInfo.getDevHubAuthInfos();
stubMethod($$.SANDBOX, AuthInfo, 'getDevHubAuthInfos').resolves([]);
stubMethod($$.SANDBOX, AuthInfo, 'listAllAuthorizations').resolves(devhubAuths);
const authInfoSaveStub = stubMethod($$.SANDBOX, AuthInfo.prototype, 'save').resolves();
const queryScratchOrgStub = stubMethod($$.SANDBOX, AuthInfo, 'queryScratchOrg');
const queryScratchOrgError = new Error('not a scratch org');
Expand All @@ -1892,7 +1947,7 @@ describe('AuthInfo', () => {

await AuthInfo.identifyPossibleScratchOrgs(authInfo.getFields(), authInfo);

expect(authInfoSaveStub.calledTwice).to.be.true;
expect(authInfoSaveStub.callCount).to.be.equal(2);
expect(authInfoSaveStub.secondCall.args[0]).to.have.property('isSandbox', true);
expect(authInfoSaveStub.secondCall.args[0]).to.have.property('isScratch', false);
expect(sandboxSetStub.calledOnce).to.be.true;
Expand Down

4 comments on commit 57c0bae

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger Benchmarks - ubuntu-latest

Benchmark suite Current: 57c0bae Previous: 9edac59 Ratio
Child logger creation 522932 ops/sec (±0.60%) 463584 ops/sec (±4.42%) 0.89
Logging a string on root logger 724285 ops/sec (±11.48%) 496874 ops/sec (±10.83%) 0.69
Logging an object on root logger 528692 ops/sec (±10.64%) 375828 ops/sec (±14.46%) 0.71
Logging an object with a message on root logger 9933 ops/sec (±198.03%) 294578 ops/sec (±10.93%) 29.66
Logging an object with a redacted prop on root logger 428107 ops/sec (±12.61%) 9695 ops/sec (±190.37%) 0.022646207606976762
Logging a nested 3-level object on root logger 295503 ops/sec (±11.59%) 242444 ops/sec (±11.01%) 0.82

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Logger Benchmarks - ubuntu-latest'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: 57c0bae Previous: 9edac59 Ratio
Logging an object with a message on root logger 9933 ops/sec (±198.03%) 294578 ops/sec (±10.93%) 29.66

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger Benchmarks - windows-latest

Benchmark suite Current: 57c0bae Previous: 9edac59 Ratio
Child logger creation 418010 ops/sec (±0.84%) 473416 ops/sec (±3.70%) 1.13
Logging a string on root logger 497549 ops/sec (±9.86%) 471884 ops/sec (±17.25%) 0.95
Logging an object on root logger 448049 ops/sec (±10.85%) 215316 ops/sec (±33.82%) 0.48
Logging an object with a message on root logger 257186 ops/sec (±18.59%) 129609 ops/sec (±37.43%) 0.50
Logging an object with a redacted prop on root logger 286271 ops/sec (±17.76%) 315326 ops/sec (±22.16%) 1.10
Logging a nested 3-level object on root logger 1439 ops/sec (±224.32%) 10741 ops/sec (±187.73%) 7.46

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Logger Benchmarks - windows-latest'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: 57c0bae Previous: 9edac59 Ratio
Logging a nested 3-level object on root logger 1439 ops/sec (±224.32%) 10741 ops/sec (±187.73%) 7.46

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.