@sap/adp-cf
Version:
cf service logic for all yeoman generators
545 lines • 25.7 kB
JavaScript
;
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