/* Copyright IBM Corp. 2017 */ import * as path from 'path'; import { Observable, Subject } from 'rxjs'; import { map, mergeMap, shareReplay, tap } from 'rxjs/operators'; import { join } from 'path'; import { Options } from './../options'; import { configGetAppDir, configGetAppName, configGetContextRoot, configGetDataDir, configReadPackageConfig, configTrimSlashes, } from './../utils/config'; import { rxMkdirp } from './../utils/rx.walk'; import { camelCase, kebabCase } from './../utils/transliteration'; import { readWchTools, wchDeliveryUrlFromApiUrl, WchTools, wchToolsGetApiBaseUrl, wchToolsGetOptions, WchToolsOptions, } from './../utils/wchtools'; import { Command } from './command'; import { AngularCliConfiguration } from '../utils/angular/angular'; import { rxReadTextFile } from '../utils/rx.file'; export abstract class AbstractCommand implements Command { private resourceRoot = path.resolve(__dirname, '../../resources'); private _logger: Subject; constructor(private abstractOptions: Options) { } private _packageDir: string; private _dataDir: string; private _appDir: string; private _packageConfig: any; private _bPackageConfig = false; private _appName: string; private _contextRoot: string; private _apiBase: Observable; private _deliveryBase: Observable; private _appAssetDir: string; private _wchToolsOptions: Observable; private _wchTools: Observable; private _angularJson: Observable; protected validateWchToolsOptions(aOpt: WchToolsOptions): boolean { return !!(aOpt && aOpt.baseUrl && aOpt.username && aOpt.password); } protected getWchTools(): Observable { if (!this._wchTools) { this._wchTools = this.rxMkdirp(this.getDataDir()).pipe( mergeMap(readWchTools), shareReplay() ); } return this._wchTools; } protected getWchToolsOptions(): Observable { if (!this._wchToolsOptions) { this._wchToolsOptions = wchToolsGetOptions(this.getOptions(), this.getDataDir()).pipe( tap(opt => { if (!this.validateWchToolsOptions(opt)) { throw new Error('Insufficient Credentials'); } }), shareReplay() ); } return this._wchToolsOptions; } protected getAngularCliConfig(): Observable { if (!this._angularJson) { this._angularJson = rxReadTextFile(join(this.getPackageDir(), 'angular.json')).pipe( map(file => JSON.parse(file)) ); } // ok return this._angularJson; } protected getContextRoot(aAppName?: string): string { if (!this._contextRoot) { // do we have a context root const configRoot = configGetContextRoot(this.getPackageDir(), this.getPackageConfig()); // if not specified, use the application name if ((configRoot === null) || (configRoot === undefined)) { // fallback to using the application name this._contextRoot = `/${encodeURIComponent(aAppName || this.getAppName())}`; } else { // use the specified root this._contextRoot = `/${configTrimSlashes(configRoot)}`; } // log this const logger = this.getLogger(); logger.next(`AppContextRoot [${this._contextRoot}].`); } // ok return this._contextRoot; } protected getAppAssetDir(aAppName?: string): string { if (!this._appAssetDir) { this._appAssetDir = path.resolve(path.normalize(path.join(this.getDataDir(), 'assets', configTrimSlashes(aAppName || this.getAppName())))); // log this const logger = this.getLogger(); logger.next(`AppAssetDirectory [${this._appAssetDir}].`); } // ok return this._appAssetDir; } protected getApiBase(): Observable { if (!this._apiBase) { this._apiBase = wchToolsGetApiBaseUrl(this.getOptions(), this.getDataDir()).pipe( tap(url => this.getLogger().next(`ApiBaseUrl [${url}].`)), shareReplay() ); } // ok return this._apiBase; } protected getDeliveryBase(): Observable { if (!this._deliveryBase) { this._deliveryBase = this.getApiBase().pipe( map(wchDeliveryUrlFromApiUrl), tap(url => this.getLogger().next(`DeliveryBaseUrl [${url}].`)), shareReplay() ); } // ok return this._deliveryBase; } protected getPackageConfig(): any { if (!this._bPackageConfig) { this._packageConfig = configReadPackageConfig(this.getPackageDir()); this._bPackageConfig = true; } // ok return this._packageConfig; } protected getAppName(): string { if (!this._appName) { // check for the option const opts = this.getOptions(); this._appName = configGetAppName(this.getPackageDir(), this.getPackageConfig()) || 'defaultApp'; // log this const logger = this.getLogger(); logger.next(`AppName [${this._appName}].`); } // ok return this._appName; } protected getAppDir(): string { if (!this._appDir) { // check for the option const opts = this.getOptions(); this._appDir = configGetAppDir(this.getPackageDir(), this.getPackageConfig()) || 'defaultApp'; // log this const logger = this.getLogger(); logger.next(`AppDirectory [${this._appDir}].`); } // ok return this._appDir; } protected getConfigValue(aKey: string): string | undefined { const config = this.getPackageConfig(); return config ? config[aKey] : undefined; } protected getDataDir(): string { if (!this._dataDir) { // check for the option const opts = this.getOptions(); this._dataDir = path.resolve(opts.data || configGetDataDir(this.getPackageDir(), this.getPackageConfig()) || '.'); // log this const logger = this.getLogger(); logger.next(`DataDirectory [${this._dataDir}].`); } // ok return this._dataDir; } protected getPackageDir(): string { if (!this._packageDir) { // check if we have the override const opts = this.getOptions(); const pkgDir = opts.pkgDir || '.'; this._packageDir = path.resolve(path.normalize(pkgDir)); // log this const logger = this.getLogger(); logger.next(`PackageDirectory [${this._packageDir}].`); } // ok return this._packageDir; } protected getCommandName(): string { return (this).constructor.name; } printHelp(): void { } protected getLogger(): Subject { if (!this._logger) { // logger const logger = new Subject(); const name = this.getCommandName(); // add some logging if (this.abstractOptions.verbose) { logger.subscribe(data => console.info(`\x1b[32m ${name}`, data, '\x1b[0m')); } // attach the logger this._logger = logger; } // ok return this._logger; } protected getOptions(): Options { return this.abstractOptions; } protected getFileName(aIdentifier: string): string { return kebabCase(camelCase(aIdentifier)); } protected rxMkdirp(aPath: string): Observable { return rxMkdirp(aPath).pipe(shareReplay()); } abstract process(): Observable; }