@spasea/uz-booking-client
Version:
Unofficial UZ api client
202 lines (201 loc) • 8.09 kB
JavaScript
;
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;