UNPKG

@spasea/uz-booking-client

Version:
202 lines (201 loc) 8.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = require("axios"); const debug = require("debug"); const uuid_1 = require("uuid"); const https = require("https"); const log = debug('uz:requestable'); const axiosInstance = axios_1.default.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false, }), }); /** * The error structure returned when a network call fails */ class ResponseError extends Error { /** * Construct a new ResponseError * @param {string} message - an message to return instead of the the default error message * @param {string} path - the requested path * @param {Object} response - the object returned by Axios */ constructor(message, path, response) { super(message); this.path = path; this.request = response.config; this.response = (response || {}).response || response; this.status = response.status; } } /** * Requestable wraps the logic for making http requests to the API */ // tslint:disable-next-line class Requestable { /** * Initialize the http internals. * @param {string} [lang] - language * @param {Object} [auth] - the credentials to authenticate to Github. If auth is * not provided request will be made unauthenticated * @param {string} [apiBase] - the base UzBooking API URL * @param {boolean} [langAsApiPrefix] - use language in url as prefix * @param userId */ constructor(lang, auth, apiBase, langAsApiPrefix = false, userId = 'guest') { this.METHODS_WITH_NO_BODY = ['GET', 'HEAD', 'DELETE']; this.apiBase = `${apiBase}${langAsApiPrefix ? lang + '/' : ''}`; this.lang = lang; this.auth = auth; this.userId = userId; } /** * Compute the URL to use to make a request. * @private * @param {string} path - either a URL relative to the API base or an absolute URL * @returns {string} - the URL to use */ getURL(path) { let url = path; if (path.indexOf('//') === -1) { url = this.apiBase + path; } return url; } /** * Compute the headers required for an API request. * @private * @param {string} dataType * @param {string} [accessToken] * @returns {Object} - the headers to use in the request */ getRequestHeaders(dataType, accessToken) { const headers = Object.assign(Object.assign(Object.assign({ Accept: 'application/json', 'Accept-Encoding': 'gzip, deflate, br' }, dataType === 'json' ? { 'Content-Type': 'application/json; charset=UTF-8' } : { 'Content-Type': 'application/x-www-form-urlencoded' }), accessToken ? { Authorization: accessToken, } : {}), { DNT: 1, // Host: 'booking.uz.gov.ua', // Origin: 'https://booking.uz.gov.ua', // Referer: 'https://booking.uz.gov.ua/ru/', // 'User-Agent': // 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36', // 'X-Requested-With': 'XMLHttpRequest' 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36', 'x-client-locale': this.lang, 'x-user-agent': `UZ/2 Web/1 User/${this.userId}` }); return headers; } /** * Make a request. * @param {string} method - the method for the request (GET, PUT, POST, DELETE) * @param {string} path - the path for the request * @param {*} [data] - the data to send to the server. For HTTP methods that don't have a body the data * @param {string} [dataType='json'] - type of data to send * will be sent as query parameters * @param {boolean} [raw=false] - if the request should be sent as raw. If this is a falsy value then the * @param {function(error, data, response)} [callback] - the callback for the request * request will be made as JSON * @returns {Promise<AxiosResponse>} - the Promise for the http request */ request(method, path, data, dataType = 'json', raw = false, callback) { const url = this.getURL(path); const AcceptHeader = (data || {}).AcceptHeader; if (AcceptHeader) { delete data.AcceptHeader; } const headers = this.getRequestHeaders(dataType, this.auth); let queryParams = {}; const shouldUseDataAsParams = data && typeof data === 'object' && this.methodHasNoBody(method); if (shouldUseDataAsParams) { queryParams = data; data = undefined; } let formatedData = null; if (data) { if (dataType === 'json') { formatedData = this.formatData(data); } else { formatedData = this.encodeUrlForm(data); } } const config = { data: formatedData, method, headers, params: queryParams, responseType: raw ? 'text' : 'json', url, }; log(`${config.method} to ${config.url}`); const requestPromise = axiosInstance(config).catch(this.callbackErrorOrThrow(path, callback)); if (callback) { requestPromise.then((response) => { if (response.data && Object.keys(response.data).length > 0) { callback(null, response.data, response); } else if (config.method !== 'GET' && Object.keys(response.data).length < 1) { callback(null, {}, response); } else { callback(null, response.data, response); } }); } return requestPromise; } callbackErrorOrThrow(path, cb) { return function handler(object) { let error; if (object.config && object.response && object.response.status && object.response.statusText) { const { response: { status, statusText }, config: { method, url }, } = object; const message = `${status} error making request ${method} ${url}: "${statusText}"`; error = new ResponseError(message, path, object); log(`${message} ${JSON.stringify(object.data)}`); } else { error = object; } if (cb) { log('going to error callback'); cb(error); } else { log('throwing error'); throw error; } }; } methodHasNoBody(method) { return this.METHODS_WITH_NO_BODY.indexOf(method) !== -1; } formatData(data) { const currentDate = new Date(); // tslint:disable-next-line: variable-name const datetime_utc = currentDate.getUTCFullYear() + '-' + ('0' + (currentDate.getUTCMonth() + 1)).slice(-2) + '-' + ('0' + currentDate.getUTCDate()).slice(-2) + ' ' + ('0' + currentDate.getUTCHours()).slice(-2) + ':' + ('0' + currentDate.getUTCMinutes()).slice(-2) + ':' + ('0' + currentDate.getUTCSeconds()).slice(-2); const randomId = (0, uuid_1.v4)(); // for (const k of Object.keys(data.data).filter((t) => ['train', 'wagon_type'].includes(t))) { // data.data[k] = encodeURIComponent(data.data[k]); // } return Object.assign(Object.assign({}, data), { datetime_utc, lang: this.lang, os: 1, request_id: randomId, version: '1.011' }); } encodeUrlForm(form) { return Object.keys(form).reduce((p, c) => p + `&${c}=${encodeURIComponent(form[c])}`, ''); } } exports.default = Requestable;