Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authorize orgs against different URLs (production, sandbox, or custom) #818

Merged
merged 15 commits into from
Dec 20, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ import {
import { ForceAuthLogoutAll } from './forceAuthLogout';

export const DEFAULT_ALIAS = 'vscodeOrg';
export const PRODUCTION_URL = 'https://login.salesforce.com';
export const SANDBOX_URL = 'https://test.salesforce.com';

export class ForceAuthWebLoginExecutor extends SfdxCommandletExecutor<Alias> {
public build(data: Alias): Command {
export class ForceAuthWebLoginExecutor extends SfdxCommandletExecutor<
AuthParams
> {
public build(data: AuthParams): Command {
return new SfdxCommandBuilder()
.withDescription(nls.localize('force_auth_web_login_authorize_org_text'))
.withArg('force:auth:web:login')
.withFlag('--setalias', data.alias)
.withFlag('--instanceurl', data.loginUrl)
.withArg('--setdefaultusername')
.withLogName('force_auth_web_login')
.build();
Expand Down Expand Up @@ -87,13 +92,14 @@ export abstract class ForceAuthDemoModeExecutor<
}

export class ForceAuthWebLoginDemoModeExecutor extends ForceAuthDemoModeExecutor<
Alias
AuthParams
> {
public build(data: Alias): Command {
public build(data: AuthParams): Command {
return new SfdxCommandBuilder()
.withDescription(nls.localize('force_auth_web_login_authorize_org_text'))
.withArg('force:auth:web:login')
.withFlag('--setalias', data.alias)
.withFlag('--instanceurl', data.loginUrl)
.withArg('--setdefaultusername')
.withArg('--noprompt')
.withJson()
Expand All @@ -102,8 +108,31 @@ export class ForceAuthWebLoginDemoModeExecutor extends ForceAuthDemoModeExecutor
}
}

export class AliasGatherer implements ParametersGatherer<Alias> {
public async gather(): Promise<CancelResponse | ContinueResponse<Alias>> {
export class AuthParamsGatherer implements ParametersGatherer<AuthParams> {
public async gather(): Promise<
CancelResponse | ContinueResponse<AuthParams>
> {
const orgTypeItems: vscode.QuickPickItem[] = [
{ label: 'Production', detail: 'login.salesforce.com' },
brpowell marked this conversation as resolved.
Show resolved Hide resolved
{ label: 'Sandbox', detail: 'test.salesforce.com' },
{ label: 'Custom', detail: 'Use a custom login URL' }
];
const selection = await vscode.window.showQuickPick(orgTypeItems);
const orgType = (selection as vscode.QuickPickItem).label;
let loginUrl: string | undefined;
if (orgType === 'Custom') {
brpowell marked this conversation as resolved.
Show resolved Hide resolved
const customUrlInputOptions = {
prompt: nls.localize('parameter_gatherer_enter_custom_url'),
placeHolder: PRODUCTION_URL
};
loginUrl = await vscode.window.showInputBox(customUrlInputOptions);
if (loginUrl === undefined) {
return { type: 'CANCEL' };
}
} else {
loginUrl = orgType === 'Sandbox' ? SANDBOX_URL : PRODUCTION_URL;
}

const aliasInputOptions = {
prompt: nls.localize('parameter_gatherer_enter_alias_name'),
placeHolder: DEFAULT_ALIAS
Expand All @@ -113,14 +142,19 @@ export class AliasGatherer implements ParametersGatherer<Alias> {
if (alias === undefined) {
return { type: 'CANCEL' };
}
return alias === ''
? { type: 'CONTINUE', data: { alias: DEFAULT_ALIAS } }
: { type: 'CONTINUE', data: { alias } };
return {
type: 'CONTINUE',
data: {
alias: alias || DEFAULT_ALIAS,
loginUrl: loginUrl || PRODUCTION_URL
}
};
}
}

export interface Alias {
export interface AuthParams {
alias: string;
loginUrl: string;
}

export async function promptLogOutForProdOrg() {
Expand All @@ -132,7 +166,7 @@ export async function promptLogOutForProdOrg() {
}

const workspaceChecker = new SfdxWorkspaceChecker();
const parameterGatherer = new AliasGatherer();
const parameterGatherer = new AuthParamsGatherer();

export function createExecutor(): SfdxCommandletExecutor<{}> {
return isDemoMode()
Expand Down
2 changes: 2 additions & 0 deletions packages/salesforcedx-vscode-core/src/messages/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export const messages = {
parameter_gatherer_enter_username_name: 'Enter target username',
parameter_gatherer_enter_alias_name:
'Enter an org alias or use default alias',
parameter_gatherer_enter_custom_url:
'Enter a custom login URL or use default URL',
brpowell marked this conversation as resolved.
Show resolved Hide resolved
parameter_gatherer_enter_scratch_org_expiration_days:
'Enter the number of days (1–30) until scratch org expiration or use the default value (7)',
parameter_gatherer_enter_project_name: 'Enter project name',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,29 @@ import { expect } from 'chai';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import {
AliasGatherer,
AuthParamsGatherer,
createExecutor,
DEFAULT_ALIAS,
ForceAuthWebLoginDemoModeExecutor,
ForceAuthWebLoginExecutor
ForceAuthWebLoginExecutor,
PRODUCTION_URL,
SANDBOX_URL
} from '../../src/commands/forceAuthWebLogin';
import { nls } from '../../src/messages';

const TEST_ALIAS = 'testAlias';
const TEST_URL = 'https://my.testdomain.salesforce.com';

// tslint:disable:no-unused-expression
describe('Force Auth Web Login', () => {
it('Should build the auth web login command', async () => {
const authWebLogin = new ForceAuthWebLoginExecutor();
const authWebLoginCommand = authWebLogin.build({ alias: TEST_ALIAS });
const authWebLoginCommand = authWebLogin.build({
alias: TEST_ALIAS,
loginUrl: TEST_URL
});
expect(authWebLoginCommand.toCommand()).to.equal(
`sfdx force:auth:web:login --setalias ${TEST_ALIAS} --setdefaultusername`
`sfdx force:auth:web:login --setalias ${TEST_ALIAS} --instanceurl ${TEST_URL} --setdefaultusername`
);
expect(authWebLoginCommand.description).to.equal(
nls.localize('force_auth_web_login_authorize_org_text')
Expand All @@ -36,41 +42,123 @@ describe('Force Auth Web Login', () => {
describe('Force Auth Web Login in Demo Mode', () => {
it('Should build the auth web login command', async () => {
const authWebLogin = new ForceAuthWebLoginDemoModeExecutor();
const authWebLoginCommand = authWebLogin.build({ alias: TEST_ALIAS });
const authWebLoginCommand = authWebLogin.build({
alias: TEST_ALIAS,
loginUrl: TEST_URL
});
expect(authWebLoginCommand.toCommand()).to.equal(
`sfdx force:auth:web:login --setalias ${TEST_ALIAS} --setdefaultusername --noprompt --json --loglevel fatal`
`sfdx force:auth:web:login --setalias ${TEST_ALIAS} --instanceurl ${TEST_URL} --setdefaultusername --noprompt --json --loglevel fatal`
);
expect(authWebLoginCommand.description).to.equal(
nls.localize('force_auth_web_login_authorize_org_text')
);
});
});

describe('Alias Gatherer', () => {
describe('Auth Params Gatherer', () => {
let inputBoxSpy: sinon.SinonStub;
let quickPickStub: sinon.SinonStub;

before(() => {
const setGathererBehavior = (
orgType: string,
customUrl: string | undefined,
orgAlias: string | undefined
) => {
quickPickStub.returns({ label: orgType });
let inputBoxCall = 0;
if (orgType === 'Custom') {
inputBoxSpy.onCall(inputBoxCall).returns(customUrl);
inputBoxCall += 1;
}
inputBoxSpy.onCall(inputBoxCall).returns(orgAlias);
};

beforeEach(() => {
inputBoxSpy = sinon.stub(vscode.window, 'showInputBox');
inputBoxSpy.onCall(0).returns(undefined);
inputBoxSpy.onCall(1).returns('');
inputBoxSpy.onCall(2).returns(TEST_ALIAS);
quickPickStub = sinon.stub(vscode.window, 'showQuickPick');
});

after(() => {
afterEach(() => {
inputBoxSpy.restore();
quickPickStub.restore();
});

it('Should return cancel if alias is undefined', async () => {
const gatherer = new AliasGatherer();
it('Should return Cancel if custom loginUrl is undefined', async () => {
setGathererBehavior('Custom', undefined, '');

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledOnce).to.be.true;
expect(response.type).to.equal('CANCEL');
});

it('Should return Continue with default alias if user input is empty string', async () => {
const gatherer = new AliasGatherer();
it('Should return Continue with production URL if custom URL user input is an empty string', async () => {
setGathererBehavior('Custom', '', '');

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledTwice).to.be.true;
if (response.type === 'CONTINUE') {
expect(response.data.loginUrl).to.equal(PRODUCTION_URL);
} else {
expect.fail('Response should be of type ContinueResponse');
}
});

it('Should return Continue with inputted URL if custom URL user input is not undefined or empty', async () => {
setGathererBehavior('Custom', TEST_URL, '');

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledTwice).to.be.true;
if (response.type === 'CONTINUE') {
expect(response.data.loginUrl).to.equal(TEST_URL);
} else {
expect.fail('Response should be of type ContinueResponse');
}
});

it('Should return Continue with production URL if Production option is chosen', async () => {
setGathererBehavior('Production', undefined, '');

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledOnce).to.be.true;
if (response.type === 'CONTINUE') {
expect(response.data.loginUrl).to.equal(PRODUCTION_URL);
} else {
expect.fail('Response should be of type ContinueResponse');
}
});

it('Should return Continue with sandbox URL if Sandbox option is chosen', async () => {
setGathererBehavior('Sandbox', undefined, '');

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledOnce).to.be.true;
if (response.type === 'CONTINUE') {
expect(response.data.loginUrl).to.equal(SANDBOX_URL);
} else {
expect.fail('Response should be of type ContinueResponse');
}
});

it('Should return Cancel if alias is undefined', async () => {
setGathererBehavior('Production', undefined, undefined);

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledOnce).to.be.true;
expect(response.type).to.equal('CANCEL');
});

it('Should return Continue with default alias if user input is empty string', async () => {
setGathererBehavior('Production', undefined, '');

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledOnce).to.be.true;
if (response.type === 'CONTINUE') {
expect(response.data.alias).to.equal(DEFAULT_ALIAS);
} else {
Expand All @@ -79,9 +167,12 @@ describe('Alias Gatherer', () => {
});

it('Should return Continue with inputted alias if user input is not undefined or empty', async () => {
const gatherer = new AliasGatherer();
setGathererBehavior('Production', undefined, TEST_ALIAS);

const gatherer = new AuthParamsGatherer();
const response = await gatherer.gather();
expect(inputBoxSpy.calledThrice).to.be.true;

expect(inputBoxSpy.calledOnce).to.be.true;
if (response.type === 'CONTINUE') {
expect(response.data.alias).to.equal(TEST_ALIAS);
} else {
Expand Down