Skip to content

Commit

Permalink
Merge pull request #452 from amtrack/refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
amtrack authored Jun 13, 2021
2 parents fb3622c + 5983d84 commit 9086b25
Show file tree
Hide file tree
Showing 67 changed files with 659 additions and 347 deletions.
8 changes: 4 additions & 4 deletions _templates/plugin/new/index.ejs.t
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const SELECTORS = {
SAVE_BUTTON: 'input[id$=":save"]'
};

export default class <%= h.changeCase.pascalCase(name) %> extends BrowserforcePlugin {
public async retrieve(definition?) {
export class <%= h.changeCase.pascalCase(name) %> extends BrowserforcePlugin {
public async retrieve(definition?: Config): Promise<Config> {
const page = await this.browserforce.openPage(PATHS.BASE);
await page.waitForSelector(SELECTORS.ENABLED);
const response = {
Expand All @@ -26,12 +26,12 @@ export default class <%= h.changeCase.pascalCase(name) %> extends BrowserforcePl
return response;
}

public async apply(config) {
public async apply(config: Config): Promise<void> {
const page = await this.browserforce.openPage(PATHS.BASE);
await page.waitForSelector(SELECTORS.ENABLED);
await page.$eval(
SELECTORS.ENABLED,
(e: HTMLInputElement, v) => {
(e: HTMLInputElement, v: boolean) => {
e.checked = v;
},
config.enabled
Expand Down
2 changes: 1 addition & 1 deletion _templates/plugin/new/plugins-index.ejs.t
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ to: src/plugins/index.ts
inject: true
before: export {
---
import * as <%= h.changeCase.camelCase(name) %> from './<%= h.changeCase.paramCase(name) %>';
import { <%= name %> as <%= h.changeCase.camelCase(name) %> } from './<%= h.changeCase.paramCase(name) %>';
26 changes: 18 additions & 8 deletions src/browserforceCommand.ts → src/browserforce-command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { core, flags, SfdxCommand } from '@salesforce/command';
import { promises } from 'fs';
import * as path from 'path';
import Browserforce from './browserforce';
import ConfigParser from './config-parser';
import { Browserforce } from './browserforce';
import { ConfigParser } from './config-parser';
import * as DRIVERS from './plugins';

core.Messages.importMessagesDirectory(__dirname);
Expand All @@ -10,7 +11,7 @@ const messages = core.Messages.loadMessages(
'browserforce'
);

export default class BrowserforceCommand extends SfdxCommand {
export class BrowserforceCommand extends SfdxCommand {
protected static requiresUsername = true;

protected static flagsConfig = {
Expand All @@ -31,13 +32,21 @@ export default class BrowserforceCommand extends SfdxCommand {
};

protected bf: Browserforce;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected settings: any[];

public async init() {
public async init(): Promise<void> {
await super.init();
const definition = await core.fs.readJson(
path.resolve(this.flags.definitionfile)
const definitionFileData = await promises.readFile(
path.resolve(this.flags.definitionfile),
'utf8'
);
let definition;
try {
definition = JSON.parse(definitionFileData);
} catch (err) {
throw new Error('Failed parsing definitionfile');
}
// TODO: use require.resolve to dynamically load plugins from npm packages
this.settings = ConfigParser.parse(DRIVERS, definition);
this.bf = new Browserforce(this.org, this.ux.cli);
Expand All @@ -46,11 +55,12 @@ export default class BrowserforceCommand extends SfdxCommand {
this.ux.stopSpinner();
}

public async run(): Promise<any> {
public async run(): Promise<unknown> {
throw new Error('BrowserforceCommand should not be run directly');
}

public async finally(err: any) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async finally(err: Error): Promise<void> {
this.ux.stopSpinner();
if (this.bf) {
this.ux.startSpinner('logging out');
Expand Down
52 changes: 35 additions & 17 deletions src/browserforce.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { core } from '@salesforce/command';
import pRetry, { AbortError } from 'p-retry';
import { Browser, launch, Page } from 'puppeteer';
import { Browser, Frame, launch, Page, WaitForOptions } from 'puppeteer';
import * as querystring from 'querystring';
import { parse, URL } from 'url';

Expand All @@ -10,17 +10,26 @@ const ERROR_DIV_SELECTOR = '#errorTitle';
const ERROR_DIVS_SELECTOR = 'div.errorMsg';
const VF_IFRAME_SELECTOR = 'iframe[name^=vfFrameId]';

export default class Browserforce {
export interface Logger {
debug(message?: unknown, ...optionalParams: unknown[]): void;
error(message?: unknown, ...optionalParams: unknown[]): void;
info(message?: unknown, ...optionalParams: unknown[]): void;
trace(message?: unknown, ...optionalParams: unknown[]): void;
warn(message?: unknown, ...optionalParams: unknown[]): void;
[m: string]: unknown;
}

export class Browserforce {
public org: core.Org;
public logger: core.Logger;
public logger: Logger;
public browser: Browser;
public page: Page;
constructor(org, logger?) {
constructor(org: core.Org, logger?: Logger) {
this.org = org;
this.logger = logger;
}

public async login() {
public async login(): Promise<Browserforce> {
this.browser = await launch({
args: [
'--no-sandbox',
Expand All @@ -39,12 +48,12 @@ export default class Browserforce {
return this;
}

public async logout() {
public async logout(): Promise<Browserforce> {
await this.browser.close();
return this;
}

public async resolveDomains() {
public async resolveDomains(): Promise<void> {
// resolve ip addresses of both LEX and classic domains
const salesforceUrls = [
this.getInstanceUrl(),
Expand All @@ -58,12 +67,15 @@ export default class Browserforce {
}
}

public async throwPageErrors(page) {
return await throwPageErrors(page);
public async throwPageErrors(page: Page): Promise<void> {
await throwPageErrors(page);
}

// path instead of url
public async openPage(urlPath, options?) {
public async openPage(
urlPath: string,
options?: WaitForOptions
): Promise<Page> {
let page;
const result = await pRetry(
async () => {
Expand Down Expand Up @@ -101,7 +113,10 @@ export default class Browserforce {
this.logger.warn('trying frontdoor workaround...');
}
// try opening page directly without frontdoor as login might have already been successful
urlPath = querystring.parse(parsedUrl.query).retURL;
const qsUrl = querystring.parse(parsedUrl.query);
urlPath = Array.isArray(qsUrl.retURL)
? qsUrl.retURL[0]
: qsUrl.retURL;
throw new Error('frontdoor error');
} else {
// the url is not as expected
Expand Down Expand Up @@ -151,14 +166,17 @@ export default class Browserforce {
// If LEX is enabled, the classic url will be opened in an iframe.
// Wait for either the selector in the page or in the iframe.
// returns the page or the frame
public async waitForSelectorInFrameOrPage(page, selector) {
public async waitForSelectorInFrameOrPage(
page: Page,
selector: string
): Promise<Page | Frame> {
await page.waitForSelector(
`pierce/force-aloha-page ${VF_IFRAME_SELECTOR}, ${VF_IFRAME_SELECTOR}, ${selector}`
);
const frameElementHandle = await page.$(
`pierce/force-aloha-page ${VF_IFRAME_SELECTOR}, ${VF_IFRAME_SELECTOR}`
);
let frameOrPage = page;
let frameOrPage: Page | Frame = page;
if (frameElementHandle) {
const frame = await frameElementHandle.contentFrame();
if (frame) {
Expand All @@ -169,7 +187,7 @@ export default class Browserforce {
return frameOrPage;
}

public getMyDomain() {
public getMyDomain(): string {
const instanceUrl = this.getInstanceUrl();
// acme.my.salesforce.com
// acme--<sandboxName>.csN.my.salesforce.com
Expand All @@ -180,7 +198,7 @@ export default class Browserforce {
return null;
}

public getInstanceDomain() {
public getInstanceDomain(): string {
const instanceUrl = this.getInstanceUrl();
// csN.salesforce.com
// acme--<sandboxName>.csN.my.salesforce.com
Expand All @@ -197,12 +215,12 @@ export default class Browserforce {
return null;
}

public getInstanceUrl() {
public getInstanceUrl(): string {
// sometimes the instanceUrl includes a trailing slash
return this.org.getConnection().instanceUrl?.replace(/\/$/, '');
}

public getLightningUrl() {
public getLightningUrl(): string {
const myDomain = this.getMyDomain();
const instanceDomain = this.getInstanceDomain();
const myDomainOrInstance = myDomain || instanceDomain;
Expand Down
6 changes: 3 additions & 3 deletions src/commands/browserforce/apply.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { core } from '@salesforce/command';
import BrowserforceCommand from '../../browserforceCommand';
import { BrowserforceCommand } from '../../browserforce-command';

core.Messages.importMessagesDirectory(__dirname);
const messages = core.Messages.loadMessages(
Expand All @@ -20,15 +20,15 @@ export default class BrowserforceApply extends BrowserforceCommand {
`
];

public async run(): Promise<any> {
public async run(): Promise<unknown> {
const logger = await core.Logger.root();
this.ux.log(
`Applying definition file ${
this.flags.definitionfile
} to org ${this.org.getUsername()}`
);
for (const setting of this.settings) {
const driver = setting.Driver.default;
const driver = setting.Driver;
const instance = new driver(this.bf, this.org);
this.ux.startSpinner(`[${driver.name}] retrieving state`);
let state;
Expand Down
6 changes: 3 additions & 3 deletions src/commands/browserforce/plan.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { core } from '@salesforce/command';
import * as path from 'path';
import BrowserforceCommand from '../../browserforceCommand';
import { BrowserforceCommand } from '../../browserforce-command';

core.Messages.importMessagesDirectory(__dirname);
const messages = core.Messages.loadMessages(
Expand All @@ -21,7 +21,7 @@ export default class BrowserforcePlanCommand extends BrowserforceCommand {
`
];

public async run(): Promise<any> {
public async run(): Promise<unknown> {
this.ux.log(
`Generating plan with definition file ${
this.flags.definitionfile
Expand All @@ -34,7 +34,7 @@ export default class BrowserforcePlanCommand extends BrowserforceCommand {
settings: {}
};
for (const setting of this.settings) {
const driver = setting.Driver.default;
const driver = setting.Driver;
const instance = new driver(this.bf, this.org);
this.ux.startSpinner(`[${driver.name}] retrieving state`);
let driverState;
Expand Down
20 changes: 18 additions & 2 deletions src/config-parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
export default class ConfigParser {
public static parse(drivers, data) {
import { BrowserforcePlugin } from './plugin';

type Drivers = {
[key: string]: unknown;
};

type Data = {
settings?: unknown;
};

type Config = {
Driver: typeof BrowserforcePlugin;
key: string;
value: unknown;
};

export class ConfigParser {
public static parse(drivers: Drivers, data: Data): Config[] {
const settings = [];
if (data && data.settings) {
for (const driverName of Object.keys(data.settings)) {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default {};
export {};
6 changes: 3 additions & 3 deletions src/jsforce-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
* workaround as the Metadata API (converted from XML) returns an object instead of an array of length 1
* @param prop result of a Metadata API call (array or object)
*/
export function ensureArray(
export function ensureArray<T>(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
prop: any
): Array<any> {
prop: T
): Array<T> {
if (Array.isArray(prop)) {
return prop;
}
Expand Down
8 changes: 4 additions & 4 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { core } from '@salesforce/command';
import * as jsonMergePatch from 'json-merge-patch';
import Browserforce from './browserforce';
import { Browserforce } from './browserforce';

export abstract class BrowserforcePlugin {
protected org: core.Org;
Expand All @@ -10,9 +10,9 @@ export abstract class BrowserforcePlugin {
this.browserforce = browserforce;
this.org = org;
}
public abstract retrieve(definition?): Promise<any>;
public diff(state, definition) {
public abstract retrieve(definition?: unknown): Promise<unknown>;
public diff(state: unknown, definition: unknown): unknown {
return jsonMergePatch.generate(state, definition);
}
public abstract apply(plan: JSON): Promise<any>;
public abstract apply(plan: unknown): Promise<unknown>;
}
8 changes: 4 additions & 4 deletions src/plugins/activity-settings/index.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as assert from 'assert';
import * as child from 'child_process';
import * as path from 'path';
import ActivitySettings from '.';
import { ActivitySettings } from '.';

describe(ActivitySettings.name, function() {
this.slow('30s');
Expand All @@ -10,7 +10,7 @@ describe(ActivitySettings.name, function() {
const enableManyWhoPrefCmd = child.spawnSync(path.resolve('bin', 'run'), [
'browserforce:apply',
'-f',
path.resolve(path.join(__dirname, 'enable-manyWhoPref.json'))
path.resolve(path.join(__dirname, 'enable-many-who-pref.json'))
]);
assert.deepStrictEqual(
enableManyWhoPrefCmd.status,
Expand All @@ -28,7 +28,7 @@ describe(ActivitySettings.name, function() {
const enableManyWhoPrefCmd2 = child.spawnSync(path.resolve('bin', 'run'), [
'browserforce:apply',
'-f',
path.resolve(path.join(__dirname, 'enable-manyWhoPref.json'))
path.resolve(path.join(__dirname, 'enable-many-who-pref.json'))
]);
assert.deepStrictEqual(
enableManyWhoPrefCmd2.status,
Expand All @@ -44,7 +44,7 @@ describe(ActivitySettings.name, function() {
const disableManyWhoPrefCmd = child.spawnSync(path.resolve('bin', 'run'), [
'browserforce:apply',
'-f',
path.resolve(path.join(__dirname, 'disable-manyWhoPref.json'))
path.resolve(path.join(__dirname, 'disable-many-who-pref.json'))
]);
assert.deepStrictEqual(
disableManyWhoPrefCmd.status,
Expand Down
Loading

0 comments on commit 9086b25

Please sign in to comment.