UNPKG

@sap/adp-cf

Version:
545 lines 25.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const CFLocal = require("@sap/cf-tools/out/src/cf-local"); const adp_common_1 = require("@sap/adp-common"); const _1 = require("./"); const messages_1 = require("../i18n/messages"); const _ = __importStar(require("lodash")); const axios_1 = __importDefault(require("axios")); class FDCService { constructor() { this.manifests = []; this.CF_HOME = "CF_HOME"; this.WIN32 = "win32"; this.HOMEDRIVE = "HOMEDRIVE"; this.HOMEPATH = "HOMEPATH"; this.TARGET = "Target"; this.ACCESS_TOKEN = "AccessToken"; this.BEARER_SPACE = "bearer "; this.ORGANIZATION_FIELDS = "OrganizationFields"; this.SPACE_FIELDS = "SpaceFields"; this.CF_FOLDER_NAME = ".cf"; this.CONFIG_JSON_FILE = "config.json"; this.API_CF = "api.cf."; this.OK = "OK"; this.HTML5_APPS_REPO = "html5-apps-repo"; this.MTA_YAML_FILE = "mta.yaml"; this.vscode = adp_common_1.ModuleResolver.resolve("vscode"); } isCfInstalled() { return __awaiter(this, void 0, void 0, function* () { let isInstalled = true; try { yield _1.CFUtils.checkForCf(); } catch (error) { isInstalled = false; } return isInstalled; }); } loadConfig() { var _a, _b, _c; let cfHome = process.env[this.CF_HOME]; if (!cfHome) { cfHome = path.join(this.getHomedir(), this.CF_FOLDER_NAME); } const configFileLocation = path.join(cfHome, this.CONFIG_JSON_FILE); let config = {}; try { const configAsString = fs.readFileSync(configFileLocation, "utf-8"); config = JSON.parse(configAsString); } catch (e) { (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.error(messages_1.Messages.CANNOT_RECIEVE_TOKEN_ERROR_MSG); } const API_CF = this.API_CF; if (config) { const result = {}; if (config[this.TARGET]) { const apiCfIndex = config[this.TARGET].indexOf(API_CF); result.url = config[this.TARGET].substring(apiCfIndex + API_CF.length); } if (config[this.ACCESS_TOKEN]) { result.token = config[this.ACCESS_TOKEN].substring(this.BEARER_SPACE.length); } if (config[this.ORGANIZATION_FIELDS]) { result.org = { name: config[this.ORGANIZATION_FIELDS].Name, guid: config[this.ORGANIZATION_FIELDS].GUID }; } if (config[this.SPACE_FIELDS]) { result.space = { name: config[this.SPACE_FIELDS].Name, guid: config[this.SPACE_FIELDS].GUID }; } this.cfConfig = result; _1.YamlUtils.spaceGuid = (_c = (_b = this.cfConfig) === null || _b === void 0 ? void 0 : _b.space) === null || _c === void 0 ? void 0 : _c.guid; } } isLoggedIn() { var _a, _b; return __awaiter(this, void 0, void 0, function* () { let isLogged = false; let orgs; yield _1.CFUtils.getAuthToken(); this.loadConfig(); if (this.cfConfig) { try { orgs = yield CFLocal.cfGetAvailableOrgs(); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Available organizations: ${JSON.stringify(orgs)}`); if (orgs.length > 0) { isLogged = true; } } catch (e) { (_b = adp_common_1.Logger.getLogger) === null || _b === void 0 ? void 0 : _b.error(`Error occured while trying to check if it is logged in: ${e === null || e === void 0 ? void 0 : e.message}`); isLogged = false; } } return isLogged; }); } isExternalLoginEnabled() { return __awaiter(this, void 0, void 0, function* () { const commands = yield this.vscode.commands.getCommands(); return commands.includes("cf.login"); }); } isLoggedInToDifferentSource(organizacion, space, apiurl) { return __awaiter(this, void 0, void 0, function* () { const isLoggedIn = yield this.isLoggedIn(); const cfConfig = this.getConfig(); const isLoggedToDifferentSource = isLoggedIn && (cfConfig.org.name !== organizacion || cfConfig.space.name !== space || cfConfig.url !== apiurl); return isLoggedToDifferentSource; }); } login(username, password, apiEndpoint) { return __awaiter(this, void 0, void 0, function* () { let isSuccessful = false; const loginResponse = yield CFLocal.cfLogin(apiEndpoint, username, password); if (loginResponse === this.OK) { isSuccessful = true; } else { throw new Error(messages_1.Messages.LOGIN_FAILED_MSG); } return isSuccessful; }); } getOrganizations() { var _a, _b; return __awaiter(this, void 0, void 0, function* () { let organizations = []; try { organizations = yield CFLocal.cfGetAvailableOrgs(); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Available organizations: ${JSON.stringify(organizations)}`); } catch (error) { (_b = adp_common_1.Logger.getLogger) === null || _b === void 0 ? void 0 : _b.error(messages_1.Messages.CANNOT_GET_ORGANIZATIONS); } return organizations; }); } getSpaces(spaceGuid) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { let spaces = []; if (spaceGuid) { try { spaces = yield CFLocal.cfGetAvailableSpaces(spaceGuid); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Available spaces: ${JSON.stringify(spaces)} for space guid: ${spaceGuid}.`); } catch (error) { (_b = adp_common_1.Logger.getLogger) === null || _b === void 0 ? void 0 : _b.error(messages_1.Messages.CANNOT_GET_SPACES); } } else { (_c = adp_common_1.Logger.getLogger) === null || _c === void 0 ? void 0 : _c.error(messages_1.Messages.INVALID_GUID_MSG); } return spaces; }); } setOrgSpace(orgName, spaceName) { return __awaiter(this, void 0, void 0, function* () { if (!orgName || !spaceName) { throw new Error(messages_1.Messages.MISSING_ORG_OR_SPACE_NAME); } yield CFLocal.cfSetOrgSpace(orgName, spaceName); this.loadConfig(); }); } getServices(projectPath) { var _a; return __awaiter(this, void 0, void 0, function* () { const services = yield this.readMta(projectPath); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Available services defined in mta.yaml: ${JSON.stringify(services)}`); return services; }); } getBaseApps(credentials, includeInvalid = false) { var _a; return __awaiter(this, void 0, void 0, function* () { const appHostIds = this.getAppHostIds(credentials); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`App Host Ids: ${JSON.stringify(appHostIds)}`); const discoveryApps = yield Promise.all(Array.from(appHostIds).map((appHostId) => __awaiter(this, void 0, void 0, function* () { try { const response = yield this.getFDCApps(appHostId); if (response.status === 200) { const results = response.data["results"]; results.forEach((result) => (result.appHostId = appHostId)); // store appHostId in order to know by which appHostId was the app selected return results; } throw new Error(messages_1.Messages.FAILED_TO_CONNECT_TO_FDC(appHostId, messages_1.Messages.HTTP_STATUS_MESSAGE(response.status.toString(), response.statusText))); } catch (error) { return [{ appHostId: appHostId, messages: [error.message] }]; } }))).then((results) => [].concat(...results)); const validatedApps = yield this.getValidatedApps(discoveryApps, credentials); return includeInvalid ? validatedApps : validatedApps.filter((app) => { var _a; return !((_a = app.messages) === null || _a === void 0 ? void 0 : _a.length); }); }); } hasApprouter(projectName, moduleNames) { return moduleNames.some((name) => name === `${projectName.toLowerCase()}-destination-content` || name === `${projectName.toLowerCase()}-approuter`); } getManifestByBaseAppId(appId) { return this.manifests.find((appManifest) => { return appManifest["sap.app"].id === appId; }); } getApprouterType() { return _1.YamlUtils.getRouterType(); } getModuleNames(mtaProjectPath) { var _a, _b; _1.YamlUtils.loadYamlContent(path.join(mtaProjectPath, this.MTA_YAML_FILE)); return ((_b = (_a = _1.YamlUtils.yamlContent) === null || _a === void 0 ? void 0 : _a.modules) === null || _b === void 0 ? void 0 : _b.map((module) => module.name)) || []; } formatDiscovery(app) { return `${app.title} (${app.appId} ${app.appVersion})`; } getConfig() { return this.cfConfig; } getBusinessServiceKeys(businessService) { var _a; return __awaiter(this, void 0, void 0, function* () { const serviceKeys = yield _1.CFUtils.getServiceInstanceKeys({ spaceGuids: [this.getConfig().space.guid], names: [businessService] }); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Available service key instance : ${JSON.stringify(serviceKeys === null || serviceKeys === void 0 ? void 0 : serviceKeys.serviceInstance)}`); return serviceKeys; }); } validateODataEndpoints(zipEntries, credentials) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { const messages = []; let xsApp, manifest; try { xsApp = this.extractXSApp(zipEntries); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`ODATA endpoints: ${JSON.stringify(xsApp)}`); } catch (error) { messages.push(error.message); return messages; } try { manifest = this.extractManifest(zipEntries); (_b = adp_common_1.Logger.getLogger) === null || _b === void 0 ? void 0 : _b.log(`Extracted manifest: ${JSON.stringify(manifest)}`); } catch (error) { messages.push(error.message); return messages; } const dataSources = manifest && _.get(manifest, ["sap.app", "dataSources"]); const routes = xsApp === null || xsApp === void 0 ? void 0 : xsApp.routes; if (dataSources && routes) { const serviceKeyEndpoints = [].concat(...credentials.map((item) => (item.endpoints ? Object.keys(item.endpoints) : []))); messages.push(...this.matchRoutesAndDatasources(dataSources, routes, serviceKeyEndpoints)); } else if (routes && !dataSources) { messages.push(messages_1.Messages.MANIFEST_MISSING_DATASOURCES); } else if (!routes && dataSources) { messages.push(messages_1.Messages.XSAPP_MISSING_ROUTES); } return messages; }); } extractXSApp(zipEntries) { let xsApp; zipEntries.forEach((item) => { if (item.entryName.endsWith("xs-app.json")) { try { xsApp = JSON.parse(item.getData().toString("utf8")); } catch (error) { throw new Error(messages_1.Messages.FAILED_TO_PARSE_XS_APP_JSON_IN_APP_ZIP(error.message)); } } }); return xsApp; } extractManifest(zipEntries) { let manifest; zipEntries.forEach((item) => { if (item.entryName.endsWith("manifest.json")) { try { manifest = JSON.parse(item.getData().toString("utf8")); } catch (error) { throw new Error(messages_1.Messages.FAILED_TO_PARSE_MANIFEST_JSON_IN_APP_ZIP(error.message)); } } }); return manifest; } matchRoutesAndDatasources(dataSources, routes, serviceKeyEndpoints) { const messages = []; routes.forEach((route) => { if (route.endpoint && !serviceKeyEndpoints.includes(route.endpoint)) { messages.push(messages_1.Messages.MANIFEST_DATASOURCE_NOT_MATCH(route.endpoint)); } }); Object.keys(dataSources).forEach((dataSourceName) => { if (!routes.some((route) => { var _a; return (_a = dataSources[dataSourceName].uri) === null || _a === void 0 ? void 0 : _a.match(this.normalizeRouteRegex(route.source)); })) { messages.push(messages_1.Messages.SERVICE_KEY_ENDPOINT_NOT_MATCH(dataSourceName)); } }); return messages; } getAppHostIds(credentials) { const appHostIds = []; credentials.forEach((credential) => { var _a; const appHostId = (_a = credential[this.HTML5_APPS_REPO]) === null || _a === void 0 ? void 0 : _a.app_host_id; if (appHostId) { appHostIds.push(appHostId.split(",").map(function (item) { return item.trim(); })); // there might be multiple appHostIds separated by comma } }); return new Set([].concat(...appHostIds)); } filterServices(businessServices) { var _a; return __awaiter(this, void 0, void 0, function* () { const serviceLabels = businessServices.map((service) => service.label).filter((label) => label); if (serviceLabels.length > 0) { const url = `/v3/service_offerings?names=${serviceLabels.join(",")}`; const json = yield _1.CFUtils.requestCfApi(url); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Filtering services. Request to: ${url}, result: ${JSON.stringify(json)}`); const businessServiceNames = new Set(businessServices.map((service) => service.label)); const result = []; json.resources.forEach((resource) => { var _a; if (businessServiceNames.has(resource.name)) { const sapService = _.get(resource, "broker_catalog.metadata.sapservice"); if (sapService && ["v2", "v4"].includes(sapService.odataversion)) { result.push(businessServices.find((service) => resource.name === service.label).name); } else { (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Service '${resource.name}' doesn't support V2/V4 Odata and will be ignored`); } } }); if (result.length > 0) { return result; } } throw new Error(messages_1.Messages.FAILED_TO_FIND_BUSINESS_SERVICES); }); } normalizeRouteRegex(value) { return new RegExp(value.replace("^/", "^(/)*").replace("/(.*)$", "(/)*(.*)$")); } getFDCRequestArguments() { const cfConfig = this.getConfig(); const fdcUrl = "https://ui5-flexibility-design-and-configuration."; const cfApiEndpoint = `https://api.cf.${cfConfig.url}`; const endpointParts = /https:\/\/api\.cf(?:\.([^-.]*)(-\d+)?(\.hana\.ondemand\.com)|(.*))/.exec(cfApiEndpoint); const options = { withCredentials: true, headers: { "Content-Type": "application/json" } }; // Public cloud let url = `${fdcUrl}cert.cfapps.${endpointParts[1]}.hana.ondemand.com`; if (!endpointParts[3]) { // Private cloud - if hana.ondemand.com missing as a part of CF api endpotint url = `${fdcUrl}sapui5flex.cfapps${endpointParts[4]}`; if (endpointParts[4].endsWith(".cn")) { // China has a special URL pattern const parts = endpointParts[4].split("."); parts.splice(2, 0, "apps"); url = `${fdcUrl}sapui5flex${parts.join(".")}`; } } if (!adp_common_1.EnvironmentUtils.isRunningInBAS() || !endpointParts[3]) { // Adding authorization token for none BAS enviourment and // for private cloud as a temporary solution until enablement of cert auth options.headers["Authorization"] = `Bearer ${cfConfig.token}`; } return { url: url, options }; } getFDCApps(appHostId) { var _a, _b, _c, _d; return __awaiter(this, void 0, void 0, function* () { const requestArguments = this.getFDCRequestArguments(); (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`App Host: ${appHostId}, request arguments: ${JSON.stringify(requestArguments)}`); const url = `${requestArguments.url}/api/business-service/discovery?appHostId=${appHostId}`; try { if (!this.isLoggedIn()) { yield CFLocal.cfGetAvailableOrgs(); this.loadConfig(); } const response = yield axios_1.default.get(url, requestArguments.options); (_b = adp_common_1.Logger.getLogger) === null || _b === void 0 ? void 0 : _b.log(`Getting FDC apps. Request url: ${url} response status: ${response.status}, response data: ${JSON.stringify(response.data)}`); return response; } catch (error) { (_c = adp_common_1.Logger.getLogger) === null || _c === void 0 ? void 0 : _c.error(`Getting FDC apps. Request url: ${url}, response status: ${(_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status}, message: ${error.message || error}`); throw new Error(messages_1.Messages.FAILED_TO_GET_FDC_APP(url, error.message || error)); } }); } getValidatedApps(discoveryApps, credentials) { return __awaiter(this, void 0, void 0, function* () { const validatedApps = []; yield Promise.all(discoveryApps.map((app) => __awaiter(this, void 0, void 0, function* () { if (!(app.messages && app.messages.length)) { const messages = yield this.validateSelectedApp(app, credentials); app.messages = messages; } validatedApps.push(app); }))); return validatedApps; }); } validateSelectedApp(appParams, credentials) { return __awaiter(this, void 0, void 0, function* () { try { const { entries, serviceInstanceGuid, manifest } = yield _1.HTML5RepoUtils.downloadAppContent(this.cfConfig.space.guid, appParams); this.manifests.push(manifest); const messages = yield this.validateSmartTemplateApplication(manifest); this.html5RepoRuntimeGuid = serviceInstanceGuid; if ((messages === null || messages === void 0 ? void 0 : messages.length) === 0) { return this.validateODataEndpoints(entries, credentials); } else { return messages; } } catch (error) { return [error.message]; } }); } validateSmartTemplateApplication(manifest) { return __awaiter(this, void 0, void 0, function* () { const messages = []; const sAppType = adp_common_1.AppUtils.getApplicationType(manifest); if (adp_common_1.AppUtils.isSupportedAppTypeForAdaptationProject(sAppType)) { if (manifest["sap.ui5"] && manifest["sap.ui5"].flexEnabled === false) { return messages.concat(messages_1.Messages.I18N_KEY_APPLICATION_DO_NOT_SUPPORT_ADAPTATION); } } else { return messages.concat(messages_1.Messages.ADAPTATIONPROJECTPLUGIN_SMARTTEMPLATE_PROJECT_CHECK); } return messages; }); } readMta(projectPath) { return __awaiter(this, void 0, void 0, function* () { if (!projectPath) { throw new Error(messages_1.Messages.NO_PROJECT_PATH_ERR_MSG); } const mtaYamlPath = path.resolve(projectPath, this.MTA_YAML_FILE); return this.getResources([mtaYamlPath]); }); } getResources(files) { return __awaiter(this, void 0, void 0, function* () { let finalList = []; yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () { const servicesList = this.getServicesForFile(file); const oDataFilteredServices = yield this.filterServices(servicesList); finalList = finalList.concat(oDataFilteredServices); }))); return finalList; }); } getServicesForFile(file) { const serviceNames = []; _1.YamlUtils.loadYamlContent(file); const parsed = _1.YamlUtils.yamlContent; if (parsed && parsed.resources && Array.isArray(parsed.resources)) { parsed.resources.forEach((resource) => { var _a; const name = _.get(resource, "parameters.service-name") || resource.name; const label = _.get(resource, "parameters.service"); if (name) { serviceNames.push({ name, label }); if (!label) { (_a = adp_common_1.Logger.getLogger) === null || _a === void 0 ? void 0 : _a.log(`Service '${name}' will be ignored without 'service' parameter`); } } }); } return serviceNames; } getHomedir() { let homedir = os.homedir(); if (process.platform === this.WIN32 && process.env[this.HOMEDRIVE] && process.env[this.HOMEPATH]) { homedir = path.join(process.env[this.HOMEDRIVE], process.env[this.HOMEPATH]); } return homedir; } } exports.default = FDCService; //# sourceMappingURL=FDCService.js.map