UNPKG

abap-adt-api

Version:

Interface to Abap Developer Tools webservice

187 lines (186 loc) 9.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.bindingDetails = exports.runQuery = exports.tableContents = exports.servicePreviewUrl = exports.parseBindingDetails = exports.parseQueryResponse = exports.decodeQueryResult = exports.extractBindingLinks = exports.parseServiceBinding = exports.TypeKinds = void 0; const AdtException_1 = require("../AdtException"); const utilities_1 = require("../utilities"); var TypeKinds; (function (TypeKinds) { TypeKinds["ANY"] = "~"; TypeKinds["CHAR"] = "C"; TypeKinds["CLASS"] = "*"; TypeKinds["CLIKE"] = "&"; TypeKinds["CSEQUENCE"] = "?"; TypeKinds["DATA"] = "#"; TypeKinds["DATE"] = "D"; TypeKinds["DECFLOAT"] = "/"; TypeKinds["DECFLOAT16"] = "a"; TypeKinds["DECFLOAT34"] = "e"; TypeKinds["DREF"] = "l"; TypeKinds["FLOAT"] = "F"; TypeKinds["HEX"] = "X"; TypeKinds["INT"] = "I"; TypeKinds["INT1"] = "b"; TypeKinds["INT8"] = "8"; TypeKinds["INT2"] = "s"; TypeKinds["INTF"] = "+"; TypeKinds["IREF"] = "m"; TypeKinds["NUM"] = "N"; TypeKinds["NUMERIC"] = "%"; TypeKinds["OREF"] = "r"; TypeKinds["PACKED"] = "P"; TypeKinds["SIMPLE"] = "$"; TypeKinds["STRING"] = "g"; TypeKinds["STRUCT1"] = "u"; TypeKinds["STRUCT2"] = "v"; TypeKinds["TABLE"] = "h"; TypeKinds["TIME"] = "T"; TypeKinds["W"] = "w"; TypeKinds["XSEQUENCE"] = "!"; TypeKinds["XSTRING"] = "y"; TypeKinds["BREF"] = "j"; })(TypeKinds = exports.TypeKinds || (exports.TypeKinds = {})); const parseServiceBinding = (xml) => { const s = (0, utilities_1.fullParse)(xml, { removeNSPrefix: true, parseAttributeValue: false }); const attrs = (0, utilities_1.xmlNodeAttr)(s.serviceBinding); for (const key of ["releaseSupported", "published", "repair", "bindingCreated"]) attrs[key] = !`${attrs[key]}`.match(/false/i); const packageRef = (0, utilities_1.xmlNodeAttr)(s.serviceBinding.packageRef); const links = s.serviceBinding.link.map(utilities_1.xmlNodeAttr); const parseService = (name) => (service) => { const { "@_version": version, "@_releaseState": releaseState } = service; const serviceDefinition = (0, utilities_1.xmlNodeAttr)(service.serviceDefinition); return { name, version, releaseState, serviceDefinition }; }; const { "@_name": serviceName } = (0, utilities_1.xmlNode)(s, "serviceBinding", "services"); const services = (0, utilities_1.xmlArray)(s, "serviceBinding", "services", "content").map(parseService(serviceName)); const parseBinding = (b) => ({ ...(0, utilities_1.xmlNodeAttr)(b), implementation: { ...(0, utilities_1.xmlNodeAttr)(b.implementation) } }); const binding = parseBinding(s.serviceBinding.binding); return { ...attrs, packageRef, links, services, binding }; }; exports.parseServiceBinding = parseServiceBinding; const extractBindingLinks = (binding) => { var _a; const url = (_a = binding.links.find(l => l.rel === "http://www.sap.com/categories/odatav2")) === null || _a === void 0 ? void 0 : _a.href; if (!url) return []; return binding.services.map(service => { const { name: servicename, version: serviceversion, serviceDefinition: { name: srvdname } } = service; const query = { servicename, serviceversion, srvdname }; return { service, query, url }; }); }; exports.extractBindingLinks = extractBindingLinks; const decodeSapDate = (raw) => new Date(`${raw.substr(0, 4)}-${raw.substr(4, 2)}-${raw.substr(6, 2)}`); const parseValue = (type, raw) => { switch (type) { case TypeKinds.DATE: return decodeSapDate(raw); case TypeKinds.DECFLOAT: case TypeKinds.DECFLOAT16: case TypeKinds.DECFLOAT34: case TypeKinds.FLOAT: case TypeKinds.NUM: case TypeKinds.NUMERIC: case TypeKinds.PACKED: return parseFloat(raw); case TypeKinds.INT: case TypeKinds.INT1: case TypeKinds.INT8: case TypeKinds.INT2: return parseInt(raw, 10); case TypeKinds.TIME: return raw; // converting to date doesn't sound like a great idea default: return raw; } }; const decodeQueryResult = (original) => { const { columns } = original; const types = new Map(); for (const c of columns) types.set(c.name, c.type); const values = original.values.map(l => { const decoded = (k) => parseValue(types.get(k), l[k]); return Object.keys(l).reduce((o, k) => { o[k] = decoded(k); return o; }, {}); }); return { columns, values }; }; exports.decodeQueryResult = decodeQueryResult; const parseColumn = (raw) => { const { "@_name": name = "", "@_type": type = "", "@_description": description, "@_keyAttribute": keyAttribute = false, "@_colType": colType, "@_isKeyFigure": isKeyFigure = false, "@_length": length = 0, } = raw.metadata; const values = (0, utilities_1.xmlArray)(raw, "dataSet", "data"); const meta = { name, type, description, keyAttribute, colType, isKeyFigure, length }; return { values, meta }; }; function parseQueryResponse(body) { const raw = (0, utilities_1.fullParse)(body, { removeNSPrefix: true, parseTagValue: false }); const fields = (0, utilities_1.xmlArray)(raw, "tableData", "columns").map(parseColumn); const columns = fields.map(c => c.meta); const longest = fields.map(f => f.values).reduce((m, l) => l.length > m.length ? l : m, []); const row = (_, i) => fields.reduce((r, f) => { return { ...r, [f.meta.name]: f.values[i] }; }, {}); const values = longest.map(row); return { columns, values }; } exports.parseQueryResponse = parseQueryResponse; const parseBindingDetails = (xml) => { var _a; const s = (0, utilities_1.fullParse)(xml, { removeNSPrefix: true, parseAttributeValue: false }); const link = (0, utilities_1.xmlNodeAttr)((_a = s === null || s === void 0 ? void 0 : s.serviceList) === null || _a === void 0 ? void 0 : _a.link); const parseCollection = (c) => { const name = c["@_name"]; const navigation = (0, utilities_1.xmlArray)(c, "navigation").map(utilities_1.xmlNodeAttr); return { name, navigation }; }; const parseService = (s) => { const base = (0, utilities_1.xmlNodeAttr)(s); const serviceInformation = (0, utilities_1.xmlNodeAttr)(s.serviceInformation); serviceInformation.collection = (0, utilities_1.xmlArray)(s, "serviceInformation", "collection").map(parseCollection); return ({ ...base, serviceInformation }); }; const services = (0, utilities_1.xmlArray)(s, "serviceList", "services").map(parseService); return { link, services }; }; exports.parseBindingDetails = parseBindingDetails; const servicePreviewUrl = (service, collectionName) => { const { serviceId, serviceInformation: { collection, url, name, version } } = service; const annotation = `${name.substr(0, 28)}_VAN`; const baseUrl = url.replace(/(https?:\/\/[^\/]+).*/, "$1"); const cn = collection.find(c => c.name === collectionName); if (!cn) return; const encrypt = (s) => s.split("").map(c => String.fromCharCode(c.charCodeAt(0) + 20)).join(""); const names = cn.navigation.map(n => n.name).join("@@"); const targets = cn.navigation.map(n => n.target).join("@@"); const rawparm = [serviceId, cn.name, names, targets, annotation, version].join("##"); return `${baseUrl}/sap/bc/adt/businessservices/odatav2/feap?feapParams=${encodeURIComponent(encrypt(rawparm))}`; }; exports.servicePreviewUrl = servicePreviewUrl; async function tableContents(h, ddicEntityName, rowNumber = 100, decode = true, sqlQuery = "") { const qs = { rowNumber, ddicEntityName }; const response = await h.request(`/sap/bc/adt/datapreview/ddic`, { qs, headers: { Accept: "application/*" }, method: "POST", body: sqlQuery }); const queryResult = parseQueryResponse(response.body); if (decode) return (0, exports.decodeQueryResult)(queryResult); return queryResult; } exports.tableContents = tableContents; async function runQuery(h, sqlQuery, rowNumber = 100, decode = true) { const qs = { rowNumber }; const response = await h.request(`/sap/bc/adt/datapreview/freestyle`, { qs, headers: { Accept: "application/*" }, method: "POST", body: sqlQuery }); const queryResult = parseQueryResponse(response.body); if (decode) return (0, exports.decodeQueryResult)(queryResult); return queryResult; } exports.runQuery = runQuery; async function bindingDetails(h, binding, index = 0) { const queries = (0, exports.extractBindingLinks)(binding); const { query: qs, url } = queries[index]; if (!qs || !url) throw (0, AdtException_1.adtException)("Binding not found"); const response = await h.request(url, { qs, headers: { Accept: "application/*" }, method: "GET" }); return (0, exports.parseBindingDetails)(response.body); } exports.bindingDetails = bindingDetails;