abap-adt-api
Version:
Interface to Abap Developer Tools webservice
187 lines (186 loc) • 9.04 kB
JavaScript
;
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;