1 | 'use strict';
|
2 |
|
3 | const requestretry = require('requestretry');
|
4 | const request = require('request');
|
5 | const Promise = require('bluebird');
|
6 | const _ = require('lodash');
|
7 | const { getRequestId, getAuthenticatedEntity } = require('./correlation');
|
8 | const { REQUEST_ID_HEADER, AUTHENTICATED_ENTITY_HEADER } = require('./headers');
|
9 | const { envVarBasedStrategy, compositionStrategy, specifcErrorCodesStrategy, RETRY_STATUS_CODES } = require('./requestStrategies');
|
10 |
|
11 | const maxAttempts = 5;
|
12 | const retryDelay = 5000;
|
13 |
|
14 | function isNetworkError(err) {
|
15 | return _.get(err, 'code') || RETRY_STATUS_CODES.includes(err.statusCode);
|
16 | }
|
17 |
|
18 | function serviceRequest(opt) {
|
19 | opt = opt || {};
|
20 |
|
21 | const serviceConfig = opt.serviceConfig;
|
22 | if (!serviceConfig) {
|
23 | throw new Error('Service config not provided');
|
24 | }
|
25 | const serviceBasePath = `${serviceConfig.protocol}://${serviceConfig.uri}:${serviceConfig.port}`;
|
26 | const path = opt.path;
|
27 | const requestOptions = opt.requestOptions || {};
|
28 | const headers = Object.assign({}, requestOptions.headers);
|
29 |
|
30 |
|
31 |
|
32 | const authenticatedEntity = opt.authenticatedEntity || getAuthenticatedEntity();
|
33 | if (authenticatedEntity) {
|
34 | headers[AUTHENTICATED_ENTITY_HEADER] =
|
35 | authenticatedEntity.encodeToBase64String();
|
36 | }
|
37 | const requestId = getRequestId();
|
38 | if (requestId) {
|
39 | headers[REQUEST_ID_HEADER] = requestId;
|
40 | }
|
41 |
|
42 | const systemOptions = {
|
43 | url: `${serviceBasePath}${path}`,
|
44 | headers
|
45 | };
|
46 | const retryOptions = opt.retryStrategy || {
|
47 | retryStrategy: (err, response = {}) => {
|
48 | return envVarBasedStrategy('HTTP_SKIP_RETRY').excludeOnEnvironmentVariableStrategy() &&
|
49 | compositionStrategy(requestretry.RetryStrategies.NetworkError,
|
50 | specifcErrorCodesStrategy)(err, response);
|
51 | },
|
52 | maxAttempts: opt.maxAttempts || maxAttempts,
|
53 | retryDelay: opt.retryDelay || retryDelay
|
54 | };
|
55 |
|
56 | if (opt.stream) {
|
57 | return request(systemOptions);
|
58 | } else {
|
59 | systemOptions.json = true;
|
60 | systemOptions.promiseFactory = (resolver) => {
|
61 | return new Promise(resolver);
|
62 | };
|
63 | return requestretry(Object.assign({}, requestOptions, systemOptions, retryOptions))
|
64 | .then((response) => {
|
65 | if (response.statusCode >= 400) {
|
66 | const error = {};
|
67 | error.statusCode = response.statusCode;
|
68 | error.code = response.code;
|
69 | error.message = `${response.statusCode} - ${JSON && JSON.stringify ? JSON.stringify(response.body) : response.body}`;
|
70 | error.attempts = response.attempts;
|
71 | error.error = response.body;
|
72 | error.response = response;
|
73 | throw error;
|
74 | } else {
|
75 | return response;
|
76 | }
|
77 | })
|
78 | .catch((err) => {
|
79 | err.isNetworkError = isNetworkError(err);
|
80 | throw err;
|
81 | });
|
82 | }
|
83 | }
|
84 |
|
85 | module.exports = serviceRequest;
|