/* Copyright IBM Corp. 2017 */ import { join } from 'path'; import { concat, noop, Observable, throwError } from 'rxjs'; import { catchError, distinct, mergeMap, mergeMapTo, shareReplay, tap } from 'rxjs/operators'; import { VError } from 'verror'; import { OPTION_AGGREGATED, OPTION_DATA, OPTION_PASSWORD, OPTION_PKG_DIR, OPTION_SOURCE, OPTION_URL, OPTION_USER, OPTION_VERBOSE, } from '../constants'; import { rxCopyDependencies } from '../utils/asset.dependencies'; import { rxCopyDir } from '../utils/rx.copy'; import { rxEmptyDir } from '../utils/rx.delete'; import { Options } from './../options'; import { rxTempDir } from './../utils/rx.walk'; import { wchExecuteWchTools, wchHidePassword, wchToolsGetOptions, WchToolsOptions } from './../utils/wchtools'; import { AbstractCommand } from './abstract.command'; import { Command } from './command'; const REMOVE_KEY_VALUE_ARGS = [OPTION_SOURCE, OPTION_DATA, OPTION_USER, OPTION_URL, OPTION_PASSWORD, OPTION_PKG_DIR]; const REMOVE_KEY_ARGS = [OPTION_VERBOSE, OPTION_AGGREGATED]; /** * Remove arguments that we interpreted * * @param aArgs args */ function _fixArguments(aArgs: string[]): string[] { // target const result: string[] = []; const len = aArgs.length; let idx = 0; while (idx < len) { // check the current argument const arg = aArgs[idx++]; // check if this is a key if (arg.startsWith('--')) { // argument key const key = arg.substr(2); if (REMOVE_KEY_VALUE_ARGS.indexOf(key) >= 0) { // remove key and value idx++; } else if (REMOVE_KEY_ARGS.indexOf(key) >= 0) { // noop } else { // add the argument result.push(arg); } } else { // add the argument result.push(arg); } } // o return result; } export abstract class AbstractWchToolsCommand extends AbstractCommand implements Command { private _wchToolsOpts: Observable; constructor(private aWchToolsOptions: Options) { super(aWchToolsOptions); } protected getWchToolsOptions(): Observable { // lazy load the options if (!this._wchToolsOpts) { this._wchToolsOpts = wchToolsGetOptions(this.getOptions(), this.getDataDir()).pipe(shareReplay(1)); } // returns the options return this._wchToolsOpts; } protected executeWchTools(aArgs: string[]): Observable { // check if we are in aggregated mode const bAggregated = !!this.aWchToolsOptions.aggregated; if (bAggregated) { // find the package dir const pkgDir = this.getPackageDir(); const pkgFile = join(pkgDir, 'package.json'); // copy the dependencies const rxDstBase = rxTempDir().pipe(shareReplay(1)); const logger = this.getLogger(); // clean up after execution const rxDelete = rxDstBase.pipe(mergeMap(dstBase => rxEmptyDir(dstBase).pipe( catchError(error => throwError(new VError(error, 'Unable to clean the temp directory [%s].', dstBase))), tap(noop, noop, () => logger.next(`Deleted temp directory [${dstBase}].`))) )); // copy the dependencies const rxCopy = rxDstBase.pipe(mergeMap(dstBase => rxCopyDependencies(pkgFile, dstBase).pipe( catchError(error => throwError(new VError(error, 'Unable to copy the aggregated data from [%s] to [%s].', pkgFile, dstBase))), tap(noop, noop, () => logger.next(`Copied the aggregated data from [${pkgFile}] to [${dstBase}].`))))); // copy our own data dir const dataDir = this.getDataDir(); const rxCopyData = rxDstBase.pipe(mergeMap(dstBase => rxCopyDir(dataDir, dstBase, true).pipe( catchError(error => throwError(new VError(error, 'Unable to copy the application data from [%s] to [%s].', dataDir, dstBase))), tap(noop, noop, () => logger.next(`Copied the application data from [${dataDir}] to [${dstBase}].`))))); const wchtoolsArgs = _fixArguments(aArgs); const rxWchTools = rxDstBase.pipe(mergeMap(dstBase => this.getWchToolsOptions().pipe( mergeMap(opts => wchExecuteWchTools(dstBase, wchtoolsArgs, this.aWchToolsOptions, opts, logger)), catchError(error => throwError(new VError(error, 'Unable to execute wchtools with arguments [%s].', JSON.stringify(wchHidePassword(wchtoolsArgs))))), tap(noop, noop, () => logger.next(`Executed wchtools.`))))); // execute return concat(rxDelete, rxCopy, rxCopyData, rxWchTools, rxDelete).pipe( distinct(), catchError(error => rxDelete.pipe(mergeMapTo(throwError(new VError(error, 'Unable to push the application.'))))), tap(noop, noop, () => logger.next(`Command complete.`))); } // default return this.getWchToolsOptions().pipe( mergeMap(opts => wchExecuteWchTools(this.getDataDir(), _fixArguments(aArgs), this.aWchToolsOptions, opts, this.getLogger())) ); } } export { _fixArguments as wchtoolsFixArguments };