@sap/modules-exploration
Version:
SAP Modules Exploration
164 lines (151 loc) • 5.48 kB
text/typescript
import { getLogger } from "@log4js-node/log4js-api";
import { IChildLogger } from "@vscode-logging/types";
import * as fsExtra from "fs-extra";
import { IExplorationOptions } from "./ExplorationOptions";
import { IModuleFilter } from "@sap/consume-services-types";
import { messages } from "./i18n/messages";
import { IModuleInfo } from "./IModuleInfo";
import _ = require("lodash");
import path = require("path");
/**
* Main module which exposes API to explore modules at runtime
*/
export class ModulesExploration {
private static baseLogger: any = getLogger(ModulesExploration.name);
private explorationOptions: IExplorationOptions;
private generator: any;
constructor(opts: IExplorationOptions, context: any, logger?: IChildLogger) {
this.generator = _.get(context, "context.generator");
// Set defaults
this.explorationOptions = {
modulesPath: null,
configPath: null,
modulesFilter: null, // [filterField: "catalog",filterVal: ["V4"]}],
};
_.assign(this.explorationOptions, opts);
if (logger !== undefined) {
ModulesExploration.baseLogger = logger.getChildLogger({
label: ModulesExploration.name,
});
ModulesExploration.baseLogger.info(
"Logger instance was received and updated."
);
}
}
/**
* Returns an array of explored modules metadata
*/
public async getExploredModules(): Promise<IModuleInfo[]> {
// Filter module according to filters option
return this._getFilteredModules();
}
/**
* Explores modules metadata and prompt them to the user using the given generator
*/
public async promptExploredModules(promptMsg?: string): Promise<string> {
let selectedModule = "";
const message = promptMsg ? promptMsg : messages.getSelectModuleMessage();
const modulesList: IModuleInfo[] = this._getFilteredModules();
const modulesNameList = _.map(modulesList, "name");
if (_.size(modulesNameList) > 0) {
const moduleTypePrompt = await this.generator.prompt([
{
type: "list",
name: "selectedModule",
message,
choices: modulesNameList,
default: modulesNameList[0],
},
]);
// Write selected module path to config file
const moduleToRun = _.find(modulesList, (gen: any) => {
return gen.name === moduleTypePrompt.selectedModule;
});
if (moduleToRun) {
selectedModule = path.join(
this.explorationOptions.modulesPath,
moduleToRun.type,
moduleToRun.path
);
}
}
return selectedModule;
}
private _getFilteredModules(): IModuleInfo[] {
const modulesList: IModuleInfo[] = [];
const modulesPath = this.explorationOptions.modulesPath;
if (modulesPath && fsExtra.existsSync(modulesPath)) {
fsExtra.readdirSync(modulesPath).forEach((moduleName: string) => {
try {
// For each module , read module.json file
// eslint-disable-next-line @typescript-eslint/no-var-requires
const modulesMetadata = require(path.join(
modulesPath,
moduleName,
"modules.json"
));
_.forIn(
modulesMetadata,
(value: any, modulesMetadataType: string) => {
_.forEach(
modulesMetadata[modulesMetadataType],
(moduleMD: any) => {
// In case module filter passed as null in options, add all modules
if (
this.explorationOptions.modulesFilter === null ||
(moduleMD && this._passFilter(moduleMD))
) {
modulesList.push({
type: moduleName,
name: moduleMD.name,
path: moduleMD.path,
entryPoint: moduleMD.entryPoint,
});
}
}
);
}
);
} catch (e) {
const errorMsg = messages.getNoModuleFoundMessage(modulesPath, e);
console.log(errorMsg);
ModulesExploration.baseLogger.debug(
this._getFilteredModules.name,
// tslint:disable-next-line
{ modulesPath: modulesPath, status: "failed", error: errorMsg }
);
}
});
}
return modulesList;
}
private _passFilter(moduleMetadata: any) {
/*Filtering of modules is implemented as a "white list" model according to the definition below.
A module will pass the defined filter when for all provided keys in the filter:
* The module defines the filter key and the value provided by the filter is found also in the module
* The module defines the filter key with empty value (meaning the module knows about the key
existence but it is not relevant for it)
*/
const moduleFeatures: IModuleFilter[] = _.get(
moduleMetadata,
"features",
{}
);
let passes = true;
_.forEach(
this.explorationOptions.modulesFilter,
(filter: IModuleFilter) => {
_.forEach(moduleFeatures, (feature) => {
if (
feature.name === filter.name &&
_.size(_.intersection(filter.value, feature.value)) === 0
) {
passes = false;
}
});
}
);
// If all filter matches (true) then return true
return passes;
}
}