@barchart/common-js
Version:
Library of common JavaScript utilities
238 lines (190 loc) • 6.48 kB
JavaScript
const axios = require('axios');
const array = require('./../../lang/array'),
assert = require('./../../lang/assert'),
attributes = require('./../../lang/attributes'),
is = require('./../../lang/is'),
promise = require('./../../lang/promise');
const Endpoint = require('./definitions/Endpoint'),
VerbType = require('./definitions/VerbType');
const FailureReason = require('./../failures/FailureReason'),
FailureType = require('./../failures/FailureType');
module.exports = (() => {
'use strict';
/**
* Invokes web service calls using an {@link Endpoint} definition.
*
* @public
*/
class Gateway {
constructor() {
}
/**
* Invokes a web service endpoint, given the payload supplied.
*
* @public
* @static
* @async
* @param {Endpoint} endpoint
* @param {*=} payload
* @returns {Promise<Object>}
*/
static async invoke(endpoint, payload) {
return Promise.resolve()
.then(() => {
assert.argumentIsRequired(endpoint, 'endpoint', Endpoint, 'Endpoint');
const pathParameters = endpoint.path.parameters;
const headerParameters = endpoint.headers.parameters;
const queryParameters = endpoint.query.parameters;
const bodyParameters = endpoint.body.parameters;
const extractParameter = (parameter) => {
return parameter.extractor(payload)
.catch((e) => {
return null;
});
};
return Promise.all([
promise.map(pathParameters, extractParameter),
promise.map(headerParameters, extractParameter),
promise.map(queryParameters, extractParameter),
promise.map(bodyParameters, extractParameter)
]).then((groups) => {
const pathValues = groups[0];
const headerValues = groups[1];
const queryValues = groups[2];
const bodyValues = groups[3];
const parameters = array.flatten([ pathParameters, headerParameters, queryParameters, bodyParameters ]);
const values = array.flatten([ pathValues, headerValues, queryValues, bodyValues ]);
const failure = values.reduce((accumulator, value, i) => {
let failure = accumulator;
const parameter = parameters[i];
if (value === null && !parameter.optional) {
if (accumulator === null) {
failure = FailureReason.forRequest({ endpoint: endpoint })
.addItem(FailureType.REQUEST_CONSTRUCTION_FAILURE, null, true);
}
failure.addItem(FailureType.REQUEST_PARAMETER_MISSING, { name: parameter.description });
}
return failure;
}, null);
if (failure !== null) {
throw failure.format();
}
return Promise.resolve({ })
.then((options) => {
const url = [ ];
url.push(endpoint.protocol.prefix);
url.push(endpoint.host);
if (endpoint.port !== endpoint.protocol.defaultPort) {
url.push(':');
url.push(endpoint.port);
}
url.push('/');
return promise.pipeline(pathValues.map((value) => (previous) => {
let encodedValue;
if (is.null(value) || is.undefined(value)) {
encodedValue = value;
} else {
encodedValue = value.toString().replace(/\//g, '%2F');
}
previous.push(encodedValue);
return previous;
}), [ ]).then((paths) => {
url.push(paths.join('/'));
return url.join('');
}).then((url) => {
options.method = verbs.get(endpoint.verb);
options.url = url;
return options;
});
}).then((options) => {
if (headerParameters.length === 0) {
return options;
}
return promise.pipeline(headerValues.map((value, i) => (accumulator) => {
const parameter = headerParameters[i];
accumulator[parameter.key] = value;
return accumulator;
}), { }).then((headers) => {
if (headers.length !== 0) {
options.headers = headers;
}
return options;
});
}).then((options) => {
if (queryParameters.length === 0) {
return options;
}
return promise.pipeline(queryValues.map((value, i) => (accumulator) => {
const parameter = queryParameters[i];
accumulator[parameter.key] = value;
return accumulator;
}), { }).then((query) => {
if (query.length !== 0) {
options.params = query;
}
return options;
});
}).then((options) => {
if (bodyParameters.length === 0) {
return options;
}
return promise.pipeline(bodyValues.map((value, i) => (accumulator) => {
const parameter = bodyParameters[i];
attributes.write(accumulator, parameter.key, value);
return accumulator;
}), { }).then((body) => {
options.data = body.body;
return options;
});
}).then((options) => {
if (endpoint.credentials) {
return Promise.all([
Promise.resolve(endpoint.credentials.usernameExtractor(payload)),
Promise.resolve(endpoint.credentials.passwordExtractor(payload))
]).then((credentials) => {
options.auth = { };
options.auth.username = credentials[0];
options.auth.password = credentials[1];
return options;
});
} else {
return options;
}
}).then((options) => {
if (endpoint.requestInterceptor) {
return endpoint.requestInterceptor.process(options, endpoint);
} else {
return options;
}
}).then((options) => {
return axios.request(options)
.then((response) => {
let responsePromise;
if (endpoint.responseInterceptor) {
responsePromise = endpoint.responseInterceptor.process(response, endpoint);
} else {
responsePromise = Promise.resolve(response);
}
return Promise.resolve(responsePromise);
}).catch((e) => {
if (endpoint.errorInterceptor) {
return endpoint.errorInterceptor.process(e, endpoint);
} else {
throw e;
}
});
});
});
});
}
toString() {
return `[Gateway]`;
}
}
const verbs = new Map();
verbs.set(VerbType.GET, 'get');
verbs.set(VerbType.DELETE, 'delete');
verbs.set(VerbType.POST, 'post');
verbs.set(VerbType.PUT, 'put');
return Gateway;
})();