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 6 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
118 changes: 107 additions & 11 deletions packages/salesforcedx-vscode-core/src/commands/forceAuthWebLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ import {
ContinueResponse,
ParametersGatherer
} from '@salesforce/salesforcedx-utils-vscode/out/src/types/index';
import * as fs from 'fs';
import * as path from 'path';
import { Observable } from 'rxjs/Observable';
import { CancellationTokenSource, workspace } from 'vscode';
import { channelService } from '../channels/index';
import { SFDX_PROJECT_FILE } from '../constants';
import { nls } from '../messages';
import { isDemoMode, isProdOrg } from '../modes/demo-mode';
import {
Expand All @@ -37,13 +40,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 +95,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 +111,90 @@ export class ForceAuthWebLoginDemoModeExecutor extends ForceAuthDemoModeExecutor
}
}

export class AliasGatherer implements ParametersGatherer<Alias> {
public async gather(): Promise<CancelResponse | ContinueResponse<Alias>> {
export class OrgTypeItem implements vscode.QuickPickItem {
public label: string;
public detail: string;
constructor(localizeLabel: string, localizeDetail: string) {
this.label = nls.localize(localizeLabel);
this.detail = nls.localize(localizeDetail);
}
}

export class AuthParamsGatherer implements ParametersGatherer<AuthParams> {
public readonly orgTypes = {
project: new OrgTypeItem('auth_project_label', 'auth_project_detail'),
production: new OrgTypeItem('auth_prod_label', 'auth_prod_detail'),
sandbox: new OrgTypeItem('auth_sandbox_label', 'auth_sandbox_detail'),
custom: new OrgTypeItem('auth_custom_label', 'auth_custom_detail')
};

public static readonly validateUrl = (url: string) => {
const expr = /https?:\/\/(.*)/;
Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you did there, this will work for internal envs too 🕺

if (expr.test(url)) {
return null;
}
return nls.localize('auth_invalid_url');
};

private getProjectUrl(): string | undefined {
brpowell marked this conversation as resolved.
Show resolved Hide resolved
let projectUrl: string | undefined;
if (workspace.rootPath) {
const sfdxProjectJsonFile = path.join(
workspace.rootPath,
brpowell marked this conversation as resolved.
Show resolved Hide resolved
SFDX_PROJECT_FILE
);
if (fs.existsSync(sfdxProjectJsonFile)) {
const sfdxProjectConfig = JSON.parse(
fs.readFileSync(sfdxProjectJsonFile, { encoding: 'utf-8' })
);
projectUrl = sfdxProjectConfig.sfdcLoginUrl;
}
}
return projectUrl;
}

public getQuickPickItems(): vscode.QuickPickItem[] {
const projectUrl = this.getProjectUrl();
const items: vscode.QuickPickItem[] = [
this.orgTypes.production,
this.orgTypes.sandbox,
this.orgTypes.custom
];
if (projectUrl) {
const { project } = this.orgTypes;
project.detail = `${nls.localize('auth_project_detail')} (${projectUrl})`;
items.unshift(project);
}
return items;
}

public async gather(): Promise<
CancelResponse | ContinueResponse<AuthParams>
> {
const quickPickItems = this.getQuickPickItems();
const selection = await vscode.window.showQuickPick(quickPickItems);
if (!selection) {
return { type: 'CANCEL' };
}

const orgType = selection.label;
let loginUrl: string | undefined;
if (orgType === this.orgTypes.custom.label) {
const customUrlInputOptions = {
prompt: nls.localize('parameter_gatherer_enter_custom_url'),
placeHolder: PRODUCTION_URL,
validateInput: AuthParamsGatherer.validateUrl
};
loginUrl = await vscode.window.showInputBox(customUrlInputOptions);
if (loginUrl === undefined) {
return { type: 'CANCEL' };
}
} else if (orgType === this.orgTypes.project.label) {
loginUrl = this.getProjectUrl();
} 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 +204,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 +228,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
13 changes: 12 additions & 1 deletion 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 Expand Up @@ -200,5 +202,14 @@ export const messages = {
telemetry_legal_dialog_button_text: 'Read more',

invalid_debug_level_id_error:
'At least one trace flag in your org doesn\'t have an associated debug level. Before you run this command again, run "sfdx force:data:soql:query -t -q "SELECT Id FROM TraceFlag WHERE DebugLevelId = null"". Then, to delete each invalid trace flag, run "sfdx force:data:record:delete -t -s TraceFlag -i 7tfxxxxxxxxxxxxxxx", replacing 7tfxxxxxxxxxxxxxxx with the ID of each trace flag without a debug level.'
'At least one trace flag in your org doesn\'t have an associated debug level. Before you run this command again, run "sfdx force:data:soql:query -t -q "SELECT Id FROM TraceFlag WHERE DebugLevelId = null"". Then, to delete each invalid trace flag, run "sfdx force:data:record:delete -t -s TraceFlag -i 7tfxxxxxxxxxxxxxxx", replacing 7tfxxxxxxxxxxxxxxx with the ID of each trace flag without a debug level.',
auth_project_label: 'Project Default',
auth_project_detail: 'Use login URL defined in sfdx-project.json',
auth_prod_label: 'Production',
auth_prod_detail: 'login.salesforce.com',
auth_sandbox_label: 'Sandbox',
auth_sandbox_detail: 'test.salesforce.com',
auth_custom_label: 'Custom',
auth_custom_detail: 'Enter a custom login URL',
auth_invalid_url: 'URL must begin with http:// or https://'
};
Loading