Skip to content

Commit

Permalink
fix: delete sandbox auth files before writing new one
Browse files Browse the repository at this point in the history
  • Loading branch information
shetzel committed Feb 22, 2024
1 parent 1071303 commit e88d780
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 16 deletions.
27 changes: 20 additions & 7 deletions src/org/org.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1136,7 +1136,9 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
private async queryLatestSandboxProcessBySandboxName(sandboxNameIn: string): Promise<SandboxProcessObject> {
const { tooling } = this.getConnection();
this.logger.debug(`QueryLatestSandboxProcessBySandboxName called with SandboxName: ${sandboxNameIn}`);
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
const queryStr = `SELECT ${sandboxProcessFields.join(
','
)} FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;

const queryResult = await tooling.query(queryStr);
this.logger.debug(queryResult, 'Return from calling queryToolingApi');
Expand Down Expand Up @@ -1381,8 +1383,20 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
oauth2Options.clientId = productionAuthFields.clientId;
}

// Before creating the AuthInfo, delete any existing auth files for the sandbox.
// This is common when refreshing sandboxes, and will cause AuthInfo to throw
// because it doesn't want to overwrite existing auth files.
const stateAggregator = await StateAggregator.getInstance();
try {
await stateAggregator.orgs.read(sandboxRes.authUserName);
await stateAggregator.orgs.remove(sandboxRes.authUserName);
} catch (e) {
// ignore since this is only for deleting existing auth files.
}

const authInfo = await AuthInfo.create({
username: sandboxRes.authUserName,
oauth2Options,
parentUsername: productionAuthFields.username,
});

Expand All @@ -1396,7 +1410,6 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {
);
// save auth info for sandbox
await authInfo.save({
...oauth2Options,
isScratch: false,
isSandbox: true,
});
Expand Down Expand Up @@ -1540,15 +1553,15 @@ export class Org extends AsyncOptionalCreatable<Org.Options> {

this.logger.debug(result, 'Result of calling sandboxAuth');
return result;
} catch (err) {
const error = err as Error;
} catch (err: unknown) {
const error = err instanceof Error ? err : SfError.wrap(isString(err) ? err : 'unknown');
// There are cases where the endDate is set before the sandbox has actually completed.
// In that case, the sandboxAuth call will throw a specific exception.
if (error?.name === 'INVALID_STATUS') {
this.logger.debug('Error while authenticating the user', error?.toString());
this.logger.debug('Error while authenticating the user:', error.message);
} else {
// If it fails for any unexpected reason, just pass that through
throw SfError.wrap(error);
// If it fails for any unexpected reason, rethrow
throw error;
}
}
}
Expand Down
132 changes: 123 additions & 9 deletions test/unit/org/orgTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { assert, expect, config as chaiConfig } from 'chai';
import { OAuth2 } from 'jsforce';
import { Transport } from 'jsforce/lib/transport';
import { SinonSpy, SinonStub } from 'sinon';
import { Org, SandboxEvents, SandboxProcessObject, SandboxUserAuthResponse } from '../../../src/org/org';
import { Org, SandboxEvents, SandboxInfo, SandboxProcessObject, SandboxUserAuthResponse } from '../../../src/org/org';
import { AuthInfo } from '../../../src/org/authInfo';
import {} from '../../../src/org/connection';
import { Connection, SingleRecordQueryErrors } from '../../../src/org/connection';
Expand Down Expand Up @@ -894,6 +894,20 @@ describe('Org Tests', () => {
],
};

const sandboxProcessFields = [
'Id',
'Status',
'SandboxName',
'SandboxInfoId',
'LicenseType',
'CreatedDate',
'CopyProgress',
'SandboxOrganization',
'SourceId',
'Description',
'EndDate',
];

let prodTestData: MockTestOrgData;
let prod: Org;

Expand Down Expand Up @@ -973,16 +987,111 @@ describe('Org Tests', () => {
SandboxName: 'test',
EndDate: '2021-19-06T20:25:46.000+0000',
} as SandboxProcessObject;
const err = new Error('could not auth');
err.name = 'INVALID_STATUS';
// @ts-expect-error - type not assignable
stubMethod<SandboxUserAuthResponse>($$.SANDBOX, prod.getConnection().tooling, 'request').throws({
name: 'INVALID_STATUS',
});
stubMethod<SandboxUserAuthResponse>($$.SANDBOX, prod.getConnection().tooling, 'request').throws(err);

// @ts-expect-error because private method
await prod.sandboxSignupComplete(sandboxResponse);
expect(logStub.callCount).to.equal(3);
// error swallowed
expect(logStub.thirdCall.args[0]).to.equal('Error while authenticating the user');
expect(logStub.thirdCall.args[0]).to.equal('Error while authenticating the user:');
});
});

describe('refreshSandbox', () => {
const sbxInfo: SandboxInfo = {
Id: '0GQ4p000000U6nFGAS',
SandboxName: 'testSbx1',
LicenseType: 'DEVELOPER',
HistoryDays: 0,
CopyChatter: false,
AutoActivate: true,
IsDeleted: false,
CreatedDate: '2024-02-16T17:06:47.000+0000',
CreatedById: '005B0000004TiUpIAK',
LastModifiedDate: '2024-02-16T17:06:47.000+0000',
LastModifiedById: '005B0000004TiUpIAK',
};
const sbxProcess = {
attributes: {
type: 'SandboxProcess',
url: '/services/data/v60.0/tooling/sobjects/SandboxProcess/0GR1Q0000004kmaWAA',
},
Id: '0GR1Q0000004kmaWAA',
Status: 'Activating',
SandboxName: 'sbxGS02',
SandboxInfoId: '0GQ1Q0000004iQDWAY',
LicenseType: 'DEVELOPER',
CreatedDate: '2024-02-21T23:06:58.000+0000',
CopyProgress: 95,
SandboxOrganization: '00DDX000000QT3W',
SourceId: null,
Description: null,
EndDate: null,
};
let updateStub: SinonStub;
let querySandboxProcessStub: SinonStub;
let pollStatusAndAuthStub: SinonStub;

beforeEach(async () => {
updateStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'update').resolves({
id: '0GQ4p000000U6nFGAS',
success: true,
});
querySandboxProcessStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'query');
pollStatusAndAuthStub = stubMethod($$.SANDBOX, prod, 'pollStatusAndAuth').resolves(sbxProcess);
});

it('will refresh the SandboxInfo sObject correctly with polling', async () => {
querySandboxProcessStub.resolves({ records: [sbxProcess] });
const expectedQuery = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE SandboxName='${
sbxInfo.SandboxName
}' ORDER BY CreatedDate DESC`;

const result = await prod.refreshSandbox(sbxInfo, { wait: Duration.seconds(30) });

expect(updateStub.calledOnce).to.be.true;
expect(updateStub.firstCall.args[0]).to.equal('SandboxInfo');
expect(updateStub.firstCall.args[1]).to.equal(sbxInfo);
expect(querySandboxProcessStub.calledOnce).to.be.true;
expect(querySandboxProcessStub.firstCall.args[0]).to.equal(expectedQuery);
expect(pollStatusAndAuthStub.calledOnce).to.be.true;
expect(result).to.equal(sbxProcess);
});

it('will refresh the SandboxInfo sObject correctly async', async () => {
querySandboxProcessStub.resolves({ records: [sbxProcess] });
const expectedQuery = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE SandboxName='${
sbxInfo.SandboxName
}' ORDER BY CreatedDate DESC`;

const result = await prod.refreshSandbox(sbxInfo, { async: true });

expect(updateStub.calledOnce).to.be.true;
expect(updateStub.firstCall.args[0]).to.equal('SandboxInfo');
expect(updateStub.firstCall.args[1]).to.equal(sbxInfo);
expect(querySandboxProcessStub.calledOnce).to.be.true;
expect(querySandboxProcessStub.firstCall.args[0]).to.equal(expectedQuery);
expect(pollStatusAndAuthStub.called).to.be.false;
expect(result).to.equal(sbxProcess);
});

it('will throw an error if it fails to update the SandboxInfo', async () => {
updateStub.restore();
updateStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'update').resolves({
error: 'duplicate value found',
success: false,
});
try {
await shouldThrow(prod.refreshSandbox(sbxInfo, { async: true }));
} catch (e) {
expect(updateStub.calledOnce).to.be.true;
expect((e as Error).message).to.include('The sandbox org refresh failed with a result of');
expect((e as Error).message).to.include('duplicate value found');
expect((e as SfError).exitCode).to.equal(1);
}
});
});

Expand Down Expand Up @@ -1058,8 +1167,9 @@ describe('Org Tests', () => {
});

describe('resumeSandbox', () => {
const expectedSoql =
'SELECT Id,Status,SandboxName,SandboxInfoId,LicenseType,CreatedDate,CopyProgress,SandboxOrganization,SourceId,Description,EndDate FROM SandboxProcess WHERE %s ORDER BY CreatedDate DESC';
const expectedSoql = `SELECT ${sandboxProcessFields.join(
','
)} FROM SandboxProcess WHERE %s ORDER BY CreatedDate DESC`;
let lifecycleSpy: SinonSpy;
let queryStub: SinonStub;
let pollStatusAndAuthSpy: SinonSpy;
Expand Down Expand Up @@ -1193,7 +1303,9 @@ describe('Org Tests', () => {
let queryStub: SinonStub;
let pollStatusAndAuthStub: SinonStub;
const sandboxNameIn = 'test-sandbox';
const queryStr = `SELECT Id, Status, SandboxName, SandboxInfoId, LicenseType, CreatedDate, CopyProgress, SandboxOrganization, SourceId, Description, EndDate FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
const queryStr = `SELECT ${sandboxProcessFields.join(
','
)} FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;

beforeEach(async () => {
queryStub = stubMethod($$.SANDBOX, prod.getConnection().tooling, 'query');
Expand Down Expand Up @@ -1250,7 +1362,9 @@ describe('Org Tests', () => {
const deletedSbxProcess = Object.assign({}, statusResult.records[0], { Status: 'Deleted' });
queryStub.resolves({ records: [deletingSbxProcess, statusResult.records[0], deletedSbxProcess] });
const where = 'name="foo"';
const expectedSoql = `SELECT Id,Status,SandboxName,SandboxInfoId,LicenseType,CreatedDate,CopyProgress,SandboxOrganization,SourceId,Description,EndDate FROM SandboxProcess WHERE ${where} ORDER BY CreatedDate DESC`;
const expectedSoql = `SELECT ${sandboxProcessFields.join(
','
)} FROM SandboxProcess WHERE ${where} ORDER BY CreatedDate DESC`;

// @ts-ignore Testing a private method
const sbxProcess = await prod.querySandboxProcess(where);
Expand Down

4 comments on commit e88d780

@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: e88d780 Previous: 7b9d593 Ratio
Child logger creation 483757 ops/sec (±1.97%) 490979 ops/sec (±0.96%) 1.01
Logging a string on root logger 862388 ops/sec (±8.58%) 814866 ops/sec (±7.57%) 0.94
Logging an object on root logger 45697 ops/sec (±182.21%) 665919 ops/sec (±6.61%) 14.57
Logging an object with a message on root logger 422703 ops/sec (±10.49%) 5229 ops/sec (±219.58%) 0.012370387719036771
Logging an object with a redacted prop on root logger 477395 ops/sec (±6.68%) 448096 ops/sec (±8.48%) 0.94
Logging a nested 3-level object on root logger 23495 ops/sec (±183.96%) 384930 ops/sec (±7.78%) 16.38

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: e88d780 Previous: 7b9d593 Ratio
Logging an object on root logger 45697 ops/sec (±182.21%) 665919 ops/sec (±6.61%) 14.57
Logging a nested 3-level object on root logger 23495 ops/sec (±183.96%) 384930 ops/sec (±7.78%) 16.38

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: e88d780 Previous: 7b9d593 Ratio
Child logger creation 327951 ops/sec (±0.61%) 349454 ops/sec (±0.53%) 1.07
Logging a string on root logger 783021 ops/sec (±7.34%) 863109 ops/sec (±8.30%) 1.10
Logging an object on root logger 621190 ops/sec (±5.55%) 596276 ops/sec (±20.41%) 0.96
Logging an object with a message on root logger 4536 ops/sec (±213.04%) 18920 ops/sec (±185.86%) 4.17
Logging an object with a redacted prop on root logger 403798 ops/sec (±15.83%) 477783 ops/sec (±6.57%) 1.18
Logging a nested 3-level object on root logger 324656 ops/sec (±4.70%) 311458 ops/sec (±4.72%) 0.96

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: e88d780 Previous: 7b9d593 Ratio
Logging an object with a message on root logger 4536 ops/sec (±213.04%) 18920 ops/sec (±185.86%) 4.17

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

Please sign in to comment.