UNPKG

@zowe/core-for-zowe-sdk

Version:

Core libraries shared by Zowe SDK packages

282 lines 15.3 kB
"use strict"; /* * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v2.0 which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v20.html * * SPDX-License-Identifier: EPL-2.0 * * Copyright Contributors to the Zowe Project. * */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Services = void 0; const imperative_1 = require("@zowe/imperative"); const ApimlConstants_1 = require("./ApimlConstants"); const JSONC = require("comment-json"); /** * Class to handle listing services on APIML gateway. * @export * @class Services */ class Services { /** * Forms a list of APIML service attributes needed to query APIML for every * REST service for every loaded command group. This information can later * be used to create every connection profile required for every loaded * command group. * * @throws {ImperativeError} When Imperative.init() has not been called * before getPluginApimlConfigs(). * @returns The APIML service attributes needed to query APIML. */ static getPluginApimlConfigs() { const apimlConfigs = []; imperative_1.ImperativeExpect.toNotBeNullOrUndefined(imperative_1.ImperativeConfig.instance.loadedConfig, "Imperative.init() must be called before getPluginApimlConfigs()"); // Get the APIML configs from the loaded imperative config for (const apimlConfig of imperative_1.ImperativeConfig.instance.loadedConfig.apimlConnLookup || []) { apimlConfigs.push(Object.assign(Object.assign({}, apimlConfig), { connProfType: apimlConfig.connProfType || imperative_1.ImperativeConfig.instance.loadedConfig.profiles[0].type, pluginName: imperative_1.ImperativeConfig.instance.hostPackageName })); } // Load APIML configs from all plugins for (const pluginCfgProps of imperative_1.PluginManagementFacility.instance.allPluginCfgProps) { for (const apimlConfig of pluginCfgProps.impConfig.apimlConnLookup || []) { apimlConfigs.push(Object.assign(Object.assign({}, apimlConfig), { connProfType: apimlConfig.connProfType || pluginCfgProps.impConfig.profiles[0].type, pluginName: pluginCfgProps.pluginName })); } } return apimlConfigs; } /** * Calls the services endpoint of the APIML gateway to obtain a list of * services that support Single Sign-On. This list is compared against a * list of APIML service attributes defined in CLI plug-in configs. When a * service's API ID is present in both lists, a profile info object is * generated to store CLI profile info for connecting to that service. * @static * @param session Session with APIML connection info * @param configs APIML service attributes defined by CLI plug-ins * @throws {ImperativeError} When session object is undefined or missing * authentication info, or the REST request fails * @returns List of objects containing profile info for APIML services * @memberof Services */ static getServicesByConfig(session, configs) { return __awaiter(this, void 0, void 0, function* () { imperative_1.Logger.getAppLogger().trace("Services.getByConfig()"); imperative_1.ImperativeExpect.toNotBeNullOrUndefined(session, "Required session must be defined"); if (session.ISession.type === "basic") { imperative_1.ImperativeExpect.toNotBeNullOrUndefined(session.ISession.user, "User name for API ML basic login must be defined."); imperative_1.ImperativeExpect.toNotBeNullOrUndefined(session.ISession.password, "Password for API ML basic login must be defined."); } else { imperative_1.ImperativeExpect.toMatchRegExp(session.ISession.tokenType, "^apimlAuthenticationToken.*", `Token type (${session.ISession.tokenType}) for API ML token login must start with 'apimlAuthenticationToken'.`); imperative_1.ImperativeExpect.toNotBeNullOrUndefined(session.ISession.tokenValue, "Token value for API ML token login must be defined."); } // Perform GET request on APIML services endpoint const services = yield imperative_1.RestClient.getExpectJSON(session, ApimlConstants_1.ApimlConstants.SERVICES_ENDPOINT); const ssoServices = services.filter(({ apiml }) => { var _a, _b; return (_b = (_a = apiml.authentication) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.supportsSso; }); const profInfos = []; // Loop through every APIML service that supports SSO for (const service of ssoServices) { // Loop through every API advertised by this service for (const apiInfo of service.apiml.apiInfo) { // Loop through any IApimlSvcAttrs object with a matching API ID for (const config of configs.filter(({ apiId }) => apiId === apiInfo.apiId)) { if (config.gatewayUrl == null || apiInfo.gatewayUrl === config.gatewayUrl) { // Update or create IApimlProfileInfo object for this service ID and profile type let profInfo = profInfos.find(({ profName, profType }) => profName === service.serviceId && profType === config.connProfType); if (profInfo == null) { profInfo = { profName: service.serviceId, profType: config.connProfType, basePaths: [], pluginConfigs: new Set(), gatewayUrlConflicts: {} }; profInfos.push(profInfo); } if (!profInfo.basePaths.includes(apiInfo.basePath)) { if (apiInfo.gatewayUrl === config.gatewayUrl || apiInfo.defaultApi) { const numGatewayUrls = new Set(new Array(...profInfo.pluginConfigs) .filter(({ gatewayUrl }) => gatewayUrl) .map(({ gatewayUrl }) => gatewayUrl)).size; profInfo.basePaths.splice(numGatewayUrls, 0, apiInfo.basePath); } else { profInfo.basePaths.push(apiInfo.basePath); } } profInfo.pluginConfigs.add(config); profInfo.gatewayUrlConflicts[config.pluginName] = [ ...profInfo.gatewayUrlConflicts[config.pluginName] || [], apiInfo.gatewayUrl ]; } } } } // Filter base paths and detect conflicts in profile info array for (const profInfo of profInfos) { // Find the set of gateway URLs common to all CLI plug-ins const commonGatewayUrls = Object.values(profInfo.gatewayUrlConflicts) .reduce((a, b) => a.filter(x => b.includes(x))); if (commonGatewayUrls.length > 0) { const serviceIdPrefix = new RegExp(`^/${profInfo.profName}/`); profInfo.basePaths = profInfo.basePaths.filter(basePath => commonGatewayUrls.includes(basePath.replace(serviceIdPrefix, ""))); profInfo.gatewayUrlConflicts = {}; } } return profInfos; }); } /** * Converts apiml profile information to team config profile objects to be stored in a zowe.config.json file * @param profileInfoList List of apiml profiles * @returns List of config profile objects * @example * Input: IApimlProfileInfo[] = [ * { * profName: 'zosmf', * profType: 'zosmf', * basePaths: [ '/zosmf/api/v1' ], * pluginConfigs: Set(1) { [IApimlSvcAttrsLoaded] }, * conflictTypes: [ 'profType' ] * }, * { * profName: 'ibmzosmf', * profType: 'zosmf', * basePaths: [ '/ibmzosmf/api/v1' ], * pluginConfigs: Set(1) { [IApimlSvcAttrsLoaded] }, * conflictTypes: [ 'profType' ] * } * ] * Output: IConfig = { * "profiles": { * "zosmf": { * "type": "zosmf", * "properties": { * "basePath": "/zosmf/api/v1" * } * }, * "ibmzosmf": { * "type": "zosmf", * "properties": { * "basePath": "/ibmzosmf/api/v1" * } * } * }, * "defaults": { * // Multiple services were detected. * // Uncomment one of the lines below to set a different default * //"zosmf": "ibmzosmf" * "zosmf": "zosmf" * } * } * @memberof Services */ static convertApimlProfileInfoToProfileConfig(profileInfoList) { const configProfile = { properties: {}, profiles: {} }; let configDefaults = {}; const conflictingDefaults = {}; // const configPlugins: Set<string> = new Set<string>(); const _genCommentsHelper = (key, elements) => { if (elements == null || elements.length === 0) return ""; return elements.reduce((all, current, _index) => { const escapeCurrent = JSON.stringify(current).slice(1, -1); return all.concat(key.includes("base") ? `\n//"${key}": "${escapeCurrent}"` : `\n//"${key}": "${escapeCurrent}",`); }, ""); }; profileInfoList === null || profileInfoList === void 0 ? void 0 : profileInfoList.forEach((profileInfo) => { // TODO Add back in the future if we want plugins in team config // profileInfo.pluginConfigs.forEach((pluginInfo: IApimlSvcAttrsLoaded) => { // configPlugins.add(pluginInfo.pluginName); // }); if (!configDefaults[profileInfo.profType]) { configDefaults[profileInfo.profType] = profileInfo.profName; } else { if (!conflictingDefaults[profileInfo.profType]) { conflictingDefaults[profileInfo.profType] = []; } conflictingDefaults[profileInfo.profType].push(profileInfo.profName); } configProfile.profiles[profileInfo.profName] = { type: profileInfo.profType, properties: {} }; const basePaths = JSON.parse(JSON.stringify(profileInfo.basePaths)); if (basePaths.length === 1) { configProfile.profiles[profileInfo.profName].properties.basePath = basePaths[0]; } else if (basePaths.length > 1) { const defaultBasePath = basePaths.shift(); const basePathConflicts = Object.keys(profileInfo.gatewayUrlConflicts); let conflictingPluginsList = ""; basePathConflicts.forEach((element) => { // The new-line before the // "element" is required in order to properly format the comment-json object conflictingPluginsList += ` // "${element}": "${profileInfo.gatewayUrlConflicts[element].join('", "')}"`; }); // Typecasting because of this issue: https://github.com/kaelzhang/node-comment-json/issues/42 const basepathConflictMessage = ` // --- // Warning: basePath conflict detected! // Different plugins require different versions of the same API. // List:${conflictingPluginsList} // ---`; const noConflictMessage = ` // Multiple base paths were detected for this service. // Uncomment one of the lines below to use a different one.`; configProfile.profiles[profileInfo.profName].properties = JSONC.parse(` { ${basePathConflicts.length > 0 ? basepathConflictMessage : noConflictMessage} ${_genCommentsHelper("basePath", basePaths)} "basePath": ${JSON.stringify(defaultBasePath)} }`); } }); // Establish keys for object map for index check within loop const defaultKeys = Object.keys(conflictingDefaults); for (const defaultKey in conflictingDefaults) { if (configDefaults[defaultKey] != null) { const trueDefault = configDefaults[defaultKey]; delete configDefaults[defaultKey]; let jsonString = ` ${JSONC.stringify(configDefaults, null, imperative_1.ConfigConstants.INDENT).slice(0, -1)}${Object.keys(configDefaults).length > 0 ? "," : ""}`; const defaultKeyIndex = defaultKeys.indexOf(defaultKey); // Logic to ensure that comment block is not duplicated if (defaultKeyIndex === 0) { jsonString += ` // Multiple services were detected. // Uncomment one of the lines below to set a different default.`; } jsonString += ` ${_genCommentsHelper(defaultKey, conflictingDefaults[defaultKey])} "${defaultKey}": ${JSON.stringify(trueDefault)}`; // Terminate the JSON string jsonString += '\n}'; configDefaults = JSONC.parse(jsonString); } } const configResult = { profiles: configProfile.profiles, defaults: configDefaults, autoStore: true }; return configResult; } } exports.Services = Services; //# sourceMappingURL=Services.js.map