UNPKG

abap-adt-api

Version:

Interface to Abap Developer Tools webservice

286 lines (285 loc) 10.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; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AdtHTTP = exports.AxiosHttpClient = exports.session_types = void 0; const axios_1 = __importStar(require("axios")); const AdtException_1 = require("./AdtException"); const _1 = require("."); const requestLogger_1 = require("./requestLogger"); const utilities_1 = require("./utilities"); const FETCH_CSRF_TOKEN = "fetch"; const CSRF_TOKEN_HEADER = "x-csrf-token"; const SESSION_HEADER = "X-sap-adt-sessiontype"; const runningInNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; let lastClientId = 0; var session_types; (function (session_types) { session_types["stateful"] = "stateful"; session_types["stateless"] = "stateless"; session_types["keep"] = ""; })(session_types = exports.session_types || (exports.session_types = {})); const toAxiosConfig = (options) => { const config = { method: options.method || "GET", url: options.url, headers: options.headers || {}, params: options.qs, httpsAgent: options.httpsAgent, timeout: options.timeout, auth: options.auth, data: options.body, adtRequestNumber: options.adtRequestNumber, adtStartTime: options.adtStartTime }; return config; }; let adtRequestNumber = 0; const convertheaders = (raw) => { if (raw instanceof axios_1.AxiosHeaders) return raw; const headers = new axios_1.AxiosHeaders(); for (const k in Object.keys(raw)) headers.set(k, raw[k]); return headers; }; class AxiosHttpClient { constructor(baseURL, config) { this.baseURL = baseURL; const conf = toAxiosConfig({ ...config }); this.axios = axios_1.default.create({ ...conf, baseURL }); } async request(options) { try { const config = toAxiosConfig(options); const { data, headers, ...rest } = await this.axios.request(config); const body = data ? ((0, utilities_1.isString)(data) ? data : `${data}`) : ""; return { body, headers: convertheaders(headers), ...rest }; } catch (error) { throw (0, _1.fromError)(error); } } } exports.AxiosHttpClient = AxiosHttpClient; class AdtHTTP { get isStateful() { return (this.stateful === session_types.stateful || (this.stateful === session_types.keep && this.currentSession === session_types.stateful)); } get stateful() { return this._stateful; } set stateful(value) { this._stateful = value; if (value !== session_types.keep) this.currentSession = value; } get csrfToken() { return this.commonHeaders[CSRF_TOKEN_HEADER] || FETCH_CSRF_TOKEN; } set csrfToken(token) { this.commonHeaders[CSRF_TOKEN_HEADER] = token; } get loggedin() { return this.csrfToken !== FETCH_CSRF_TOKEN; } constructor(baseURLOrClient, username, password, client, language, config) { this.username = username; this.client = client; this.language = language; this.isClone = false; this.currentSession = session_types.stateless; this._stateful = session_types.stateless; this.needKeepalive = false; this.cookie = new Map(); this.keep_session = async () => { if (this.needKeepalive && this.loggedin) await this._request("/sap/bc/adt/compatibility/graph", {}).catch(() => { }); this.needKeepalive = true; }; if (!(baseURLOrClient && username && (password || !(0, utilities_1.isString)(baseURLOrClient)))) throw (0, _1.adtException)("Invalid ADTClient configuration: url, login and password are required"); this.baseURL = (0, utilities_1.isString)(baseURLOrClient) ? baseURLOrClient : ""; this.id = lastClientId++; if ((0, utilities_1.isString)(password)) this.password = password; else this.getToken = password; this.commonHeaders = { ...config === null || config === void 0 ? void 0 : config.headers, Accept: "*/*", "Cache-Control": "no-cache", [CSRF_TOKEN_HEADER]: FETCH_CSRF_TOKEN }; this.httpclient = (0, utilities_1.isString)(baseURLOrClient) ? new AxiosHttpClient(baseURLOrClient, config) : baseURLOrClient; this.debugCallback = config === null || config === void 0 ? void 0 : config.debugCallback; if (config === null || config === void 0 ? void 0 : config.keepAlive) this.keepAlive = setInterval(() => this.keep_session(), 120000); } async login() { if (this.loginPromise) return this.loginPromise; this.cookie.clear(); // oauth if (this.getToken && !this.bearer) { await this.getToken().then(bearer => (this.bearer = bearer)); } else this.auth = { username: this.username || "", password: this.password || "" }; const qs = {}; if (this.client) qs["sap-client"] = this.client; if (this.language) qs["sap-language"] = this.language; this.csrfToken = FETCH_CSRF_TOKEN; try { this.loginPromise = this._request("/sap/bc/adt/compatibility/graph", { qs }); await this.loginPromise; } finally { this.loginPromise = undefined; } } ascookies() { return [...this.cookie.values()].join("; "); } async logout() { this.stateful = session_types.stateless; await this._request("/sap/public/bc/icf/logoff", {}); // prevent autologin this.auth = undefined; this.bearer = undefined; // new cookie jar this.cookie.clear(); // clear token this.csrfToken = FETCH_CSRF_TOKEN; } async dropSession() { this.stateful = session_types.stateless; await this._request("/sap/bc/adt/compatibility/graph", {}); } async request(url, config) { let autologin = false; try { if (!this.loggedin) { autologin = true; await this.login(); } return await this._request(url, config || {}); } catch (e) { const adtErr = (0, AdtException_1.fromException)(e, config); // if the logon ticket expired try to logon again, unless in stateful mode // or already tried a login if ((0, _1.isLoginError)(adtErr) && !autologin && !this.isStateful) { try { this.csrfToken = FETCH_CSRF_TOKEN; await this.login(); return await this._request(url, config || {}); } catch (e2) { throw (0, AdtException_1.fromException)(e2, config); } } else throw adtErr; } } updateCookies(response) { if (runningInNode) { const cookies = response.headers["set-cookie"] || []; cookies.forEach(cookie => { const cleaned = cookie .replace(/path=\/,/g, "") .replace(/path=\//g, "") .split(";")[0]; const [key] = cookie.split("=", 1); this.cookie.set(key, cleaned); }); } } logResponse(exceptionOrResponse, options) { if (!this.debugCallback) return; if ((0, _1.isAdtException)(exceptionOrResponse)) (0, requestLogger_1.logError)(this.id, exceptionOrResponse, this.debugCallback, options); else (0, requestLogger_1.logResponse)(this.id, exceptionOrResponse, options, this.debugCallback); } /** * HTTP request without automated login / retry * * @param url URL suffix * @param options request options */ async _request(url, options) { this.needKeepalive = false; const headers = { ...this.commonHeaders, ...options.headers }; headers[SESSION_HEADER] = this.stateful; if (!headers["Cookie"] && runningInNode) headers["Cookie"] = this.ascookies(); adtRequestNumber++; const adtStartTime = new Date(); const config = { ...options, auth: this.auth, headers, adtStartTime, adtRequestNumber, url }; try { if (this.getToken && !this.bearer) this.bearer = await this.getToken(); if (this.bearer) headers.Authorization = `bearer ${this.bearer}`; const response = await this.httpclient.request(config); this.updateCookies(response); if (response.status >= 400) throw (0, AdtException_1.fromException)(response, config); if (this.csrfToken === FETCH_CSRF_TOKEN && (0, utilities_1.isString)(response.headers[CSRF_TOKEN_HEADER])) this.csrfToken = response.headers[CSRF_TOKEN_HEADER]; this.logResponse(response, config); return response; } catch (error) { const exc = (0, AdtException_1.fromException)(error, config); this.logResponse(exc, config); throw exc; } } } exports.AdtHTTP = AdtHTTP;