@ducatus/ducatus-wallet-client-rev
Version:
Client for @ducatus/ducatus-wallet-service-rev
254 lines (215 loc) • 6.02 kB
text/typescript
import * as _ from 'lodash';
import { Utils } from './common';
const request = require('superagent');
const async = require('async');
const Package = require('../../package.json');
var log = require('./log');
const util = require('util');
var Errors = require('./errors');
export class Request {
baseUrl: any;
session: any;
r: any;
credentials: any;
supportStaffWalletId: any;
timeout: any;
constructor(url?, opts?) {
this.baseUrl = url;
// request can be overload only for testing
this.r = opts.r || request;
this.session = null;
this.credentials = null;
}
setCredentials(credentials) {
this.credentials = credentials;
}
getHeaders(method, url, args) {
var headers = {
'x-client-version': 'bwc-' + Package.version
};
if (this.supportStaffWalletId) {
headers['x-wallet-id'] = this.supportStaffWalletId;
}
return headers;
}
// Sign an HTTP request
// @private
// @static
// @memberof Client.API
// @param {String} method - The HTTP method
// @param {String} url - The URL for the request
// @param {Object} args - The arguments in case this is a POST/PUT request
// @param {String} privKey - Private key to sign the request
static _signRequest(method, url, args, privKey) {
var message = [method.toLowerCase(), url, JSON.stringify(args)].join('|');
return Utils.signMessage(message, privKey);
}
// Do an HTTP request
// @private
//
// @param {Object} method
// @param {String} url
// @param {Object} args
// @param {Callback} cb
doRequest(method, url, args, useSession, cb) {
var headers = this.getHeaders(method, url, args);
if (this.credentials) {
headers['x-identity'] = this.credentials.copayerId;
if (useSession && this.session) {
headers['x-session'] = this.session;
} else {
var reqSignature;
var key = args._requestPrivKey || this.credentials.requestPrivKey;
if (key) {
delete args['_requestPrivKey'];
reqSignature = Request._signRequest(method, url, args, key);
}
headers['x-signature'] = reqSignature;
}
}
var r = this.r[method](this.baseUrl + url);
r.accept('json');
_.each(headers, (v, k) => {
if (v) r.set(k, v);
});
if (args) {
if (method == 'post' || method == 'put') {
r.send(args);
} else {
r.query(args);
}
}
r.timeout(this.timeout);
r.end((err, res) => {
if (!res) {
return cb(new Errors.CONNECTION_ERROR());
}
if (res.body)
log.debug(
util.inspect(res.body, {
depth: 10
})
);
if (res.status !== 200) {
if (res.status === 503) return cb(new Errors.MAINTENANCE_ERROR());
if (res.status === 404) return cb(new Errors.NOT_FOUND());
if (!res.status) return cb(new Errors.CONNECTION_ERROR());
log.error('HTTP Error:' + res.status);
if (!res.body) return cb(new Error(res.status));
return cb(Request._parseError(res.body));
}
if (res.body === '{"error":"read ECONNRESET"}') return cb(new Errors.ECONNRESET_ERROR(JSON.parse(res.body)));
return cb(null, res.body, res.header);
});
}
// Parse errors
// @private
// @static
// @memberof Client.API
// @param {Object} body
static _parseError(body) {
if (!body) return;
if (_.isString(body)) {
try {
body = JSON.parse(body);
} catch (e) {
body = {
error: body
};
}
}
var ret;
if (body.code) {
if (Errors[body.code]) {
ret = new Errors[body.code]();
if (body.message) ret.message = body.message;
} else {
ret = new Error(body.code + ': ' + (_.isObject(body.message) ? JSON.stringify(body.message) : body.message));
}
} else {
ret = new Error(body.error || JSON.stringify(body));
}
log.error(ret);
return ret;
}
// Do a POST request
// @private
//
// @param {String} url
// @param {Object} args
// @param {Callback} cb
post(url, args, cb) {
args = args || {};
return this.doRequest('post', url, args, false, cb);
}
put(url, args, cb) {
args = args || {};
return this.doRequest('put', url, args, false, cb);
}
// Do a GET request
// @private
//
// @param {String} url
// @param {Callback} cb
get(url, cb) {
url += url.indexOf('?') > 0 ? '&' : '?';
url += 'r=' + _.random(10000, 99999);
return this.doRequest('get', url, {}, false, cb);
}
getWithLogin(url, cb) {
url += url.indexOf('?') > 0 ? '&' : '?';
url += 'r=' + _.random(10000, 99999);
return this.doRequestWithLogin('get', url, {}, cb);
}
_login(cb) {
this.post('/v1/login', {}, cb);
}
logout(cb) {
this.post('/v1/logout', {}, cb);
}
// Do an HTTP request
// @private
//
// @param {Object} method
// @param {String} url
// @param {Object} args
// @param {Callback} cb
doRequestWithLogin(method, url, args, cb) {
async.waterfall(
[
next => {
if (this.session) return next();
this.doLogin(next);
},
next => {
this.doRequest(method, url, args, true, (err, body, header) => {
if (err && err instanceof Errors.NOT_AUTHORIZED) {
this.doLogin(err => {
if (err) return next(err);
return this.doRequest(method, url, args, true, next);
});
}
next(null, body, header);
});
}
],
cb
);
}
doLogin(cb) {
this._login((err, s) => {
if (err) return cb(err);
if (!s) return cb(new Errors.NOT_AUTHORIZED());
this.session = s;
cb();
});
}
// Do a DELETE request
// @private
//
// @param {String} url
// @param {Callback} cb
delete(url, cb) {
return this.doRequest('delete', url, {}, false, cb);
}
}