@citadelgroup/reporting-cli
Version:
Command line interface to develop custom reports for LeanIX EAM
126 lines (106 loc) • 4.3 kB
text/typescript
import * as chalk from 'chalk';
import { spawn } from 'cross-spawn';
import * as jwtDecode from 'jwt-decode';
import * as _ from 'lodash';
import * as opn from 'opn';
import { ApiTokenResolver } from './api-token-resolver';
import { loadLxrConfig } from './file.helpers';
import { LxrConfig } from './interfaces';
interface DevServerStartResult {
launchUrl: string;
localhostUrl: string;
}
export class DevStarter {
public start(): Promise<void> {
const config = loadLxrConfig();
return this.getAccessToken(config)
.then((accessToken) => this.startLocalServer(config, accessToken))
.then((result) => {
if (result) {
this.openUrlInBrowser(result.launchUrl);
console.log(chalk.green(`Open the following url to test your report:\n${result.launchUrl}`) + '\n');
console.log(
chalk.yellow(
`If your report is not being loaded, please check if it opens outside of LeanIX via this url:\n${result.localhostUrl}`
)
);
}
});
}
private async startLocalServer(config: LxrConfig, accessToken?: string): Promise<DevServerStartResult> {
const port = config.localPort || 8080;
const localhostUrl = `https://localhost:${port}`;
const urlEncoded = encodeURIComponent(localhostUrl);
const host = 'https://' + config.host;
const accessTokenHash = accessToken ? `#access_token=${accessToken}` : '';
const workspace = accessToken ? this.getWorkspaceFromAccesToken(accessToken) : config.workspace;
if (_.isEmpty(workspace)) {
console.error(chalk.red("Workspace not specified. The local server can't be started."));
return new Promise(null);
}
console.log(chalk.green(`Your workspace is ${workspace}`));
const baseLaunchUrl = `${host}/${workspace}/reports/dev?url=${urlEncoded}`;
const launchUrl = baseLaunchUrl + accessTokenHash;
console.log(chalk.green('Starting development server and launching with url: ' + baseLaunchUrl));
const args = ['--https', '--port', '' + port, '--mode=development'];
if (config.ssl && config.ssl.cert && config.ssl.key) {
args.push('--cert=' + config.ssl.cert);
args.push('--key=' + config.ssl.key);
}
console.log('' + args.join(' '));
const wpMajorVersion = await this.getCurrentWebpackMajorVersion();
const serverProcess =
wpMajorVersion === 5 ? spawn('node_modules/.bin/webpack', ['serve', ...args]) : spawn('node_modules/.bin/webpack-dev-server', args);
serverProcess.stdout.on('data', (data) => {
console.log(data.toString());
});
// output errors from webpack
serverProcess.stderr.on('data', (data) => {
console.error(chalk.red(data.toString()));
});
return new Promise<DevServerStartResult>((resolve) => {
let projectRunning = false;
serverProcess.on('error', (err) => {
console.error(err);
});
serverProcess.stdout.on('data', (data) => {
const output: string = data.toString();
if (output.indexOf('Project is running') >= 0) {
projectRunning = true;
}
if (projectRunning && output.indexOf('Compiled successfully') >= 0) {
resolve({ launchUrl, localhostUrl });
}
});
});
}
private getCurrentWebpackMajorVersion(): Promise<number> {
return new Promise((resolve) => {
const webpackVersion = spawn('node_modules/.bin/webpack', ['-v']);
webpackVersion.stdout.on('data', (data) => {
const output: string = data.toString();
const matches = output.match(/(\d+)\.\d+\.\d+/);
const majorVersion = Number.parseInt(matches[0]);
resolve(majorVersion);
});
});
}
private getWorkspaceFromAccesToken(accessToken: string) {
const claims = jwtDecode(accessToken);
return claims.principal.permission.workspaceName;
}
private getAccessToken(config: LxrConfig): Promise<string> {
if (!_.isEmpty(config.apitoken)) {
return ApiTokenResolver.getAccessToken('https://' + config.host, config.apitoken, config.proxyURL);
} else {
return Promise.resolve(null);
}
}
private openUrlInBrowser(url: string) {
try {
opn(url);
} catch (err) {
console.error('Unable to open your browser: ' + err);
}
}
}