caccl-send-request 1.0.15 → 2.0.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -8
- package/lib/ErrorCode.d.ts +5 -0
- package/lib/ErrorCode.js +11 -0
- package/lib/ErrorCode.js.map +1 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.js +164 -0
- package/lib/index.js.map +1 -0
- package/package.json +14 -10
- package/src/ErrorCode.ts +8 -0
- package/src/index.ts +140 -0
- package/tsconfig.json +19 -0
- package/errorCodes.js +0 -6
- package/index.js +0 -129
package/README.md
CHANGED
|
@@ -8,12 +8,6 @@ The default request sender used throughout the CACCL project.
|
|
|
8
8
|
**C**onnection
|
|
9
9
|
**L**ibrary
|
|
10
10
|
|
|
11
|
-
## Overriding caccl-send-request
|
|
12
|
-
|
|
13
|
-
Throughout the `caccl` project, the default sendRequest function is this `caccl-send-request` module.
|
|
14
|
-
|
|
15
|
-
However, if you want to override the sendRequest function, just create your own function that follows the description below. Your function should follow our description _exactly_, including sending of cross-origin credentials.
|
|
16
|
-
|
|
17
11
|
## Description
|
|
18
12
|
|
|
19
13
|
Sends an http request, handles paging, retries failed requests, and processes the response.
|
|
@@ -28,7 +22,7 @@ headers | object | headers to include in the request | {}
|
|
|
28
22
|
numRetries | number | number of times to retry the request if it fails | 0
|
|
29
23
|
ignoreSSLIssues | boolean | if true, ignores self-signed certificate issues | false usually, true if host is `localhost:8088`
|
|
30
24
|
|
|
31
|
-
Returns:
|
|
25
|
+
Returns:
|
|
32
26
|
`Promise.<CACCLError|object>` Returns promise that resolves with `{ body, status, headers }` on success, rejects with CACCLError (see `caccl-error` on npm) on failure.
|
|
33
27
|
|
|
34
|
-
**Note:** This function sends cross-origin credentials if `process.env.
|
|
28
|
+
**Note:** This function sends cross-origin credentials if `process.env.NODE_ENV` equals `development`.
|
package/lib/ErrorCode.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Highest error code: CSR2
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
var ErrorCode;
|
|
5
|
+
(function (ErrorCode) {
|
|
6
|
+
ErrorCode["NotConnected"] = "CSR1";
|
|
7
|
+
ErrorCode["SelfSigned"] = "CSR2";
|
|
8
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
9
|
+
;
|
|
10
|
+
exports.default = ErrorCode;
|
|
11
|
+
//# sourceMappingURL=ErrorCode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorCode.js","sourceRoot":"","sources":["../src/ErrorCode.ts"],"names":[],"mappings":";AAAA,2BAA2B;;AAE3B,IAAK,SAGJ;AAHD,WAAK,SAAS;IACZ,kCAAqB,CAAA;IACrB,gCAAmB,CAAA;AACrB,CAAC,EAHI,SAAS,KAAT,SAAS,QAGb;AAAA,CAAC;AAEF,kBAAe,SAAS,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sends and retries an http request
|
|
3
|
+
* @author Gabriel Abrams
|
|
4
|
+
* @param opts object containing all arguments
|
|
5
|
+
* @param opts.path path to send request to
|
|
6
|
+
* @param [opts.host] host to send request to
|
|
7
|
+
* @param [opts.method=GET] http method to use
|
|
8
|
+
* @param [opts.params] body/data to include in the request
|
|
9
|
+
* @param [opts.headers] headers to include in the request
|
|
10
|
+
* @param [opts.numRetries=0] number of times to retry the request if it
|
|
11
|
+
* fails
|
|
12
|
+
* @param [opts.ignoreSSLIssues] if true, ignores SSL certificate
|
|
13
|
+
* issues. If host is localhost:8088, this will default to true
|
|
14
|
+
* @returns { body, status, headers } on success
|
|
15
|
+
*/
|
|
16
|
+
declare const sendRequest: (opts: {
|
|
17
|
+
path: string;
|
|
18
|
+
host?: string;
|
|
19
|
+
method?: ('GET' | 'POST' | 'PUT' | 'DELETE');
|
|
20
|
+
params?: {
|
|
21
|
+
[x: string]: any;
|
|
22
|
+
};
|
|
23
|
+
headers?: {
|
|
24
|
+
[x: string]: any;
|
|
25
|
+
};
|
|
26
|
+
numRetries?: number;
|
|
27
|
+
ignoreSSLIssues?: boolean;
|
|
28
|
+
}) => Promise<{
|
|
29
|
+
body: any;
|
|
30
|
+
status: number;
|
|
31
|
+
headers: {
|
|
32
|
+
[x: string]: any;
|
|
33
|
+
};
|
|
34
|
+
}>;
|
|
35
|
+
export default sendRequest;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
24
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (_) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
|
+
};
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
// Import libs
|
|
54
|
+
var axios_1 = __importDefault(require("axios"));
|
|
55
|
+
var qs_1 = __importDefault(require("qs"));
|
|
56
|
+
// Import other CACCL libs
|
|
57
|
+
var caccl_error_1 = __importDefault(require("caccl-error"));
|
|
58
|
+
// Import shared types
|
|
59
|
+
var ErrorCode_1 = __importDefault(require("./ErrorCode"));
|
|
60
|
+
// Check if we should send cross-domain credentials
|
|
61
|
+
var sendCrossDomainCredentials = (process.env.NODE_ENV === 'development');
|
|
62
|
+
/**
|
|
63
|
+
* Sends and retries an http request
|
|
64
|
+
* @author Gabriel Abrams
|
|
65
|
+
* @param opts object containing all arguments
|
|
66
|
+
* @param opts.path path to send request to
|
|
67
|
+
* @param [opts.host] host to send request to
|
|
68
|
+
* @param [opts.method=GET] http method to use
|
|
69
|
+
* @param [opts.params] body/data to include in the request
|
|
70
|
+
* @param [opts.headers] headers to include in the request
|
|
71
|
+
* @param [opts.numRetries=0] number of times to retry the request if it
|
|
72
|
+
* fails
|
|
73
|
+
* @param [opts.ignoreSSLIssues] if true, ignores SSL certificate
|
|
74
|
+
* issues. If host is localhost:8088, this will default to true
|
|
75
|
+
* @returns { body, status, headers } on success
|
|
76
|
+
*/
|
|
77
|
+
var sendRequest = function (opts) { return __awaiter(void 0, void 0, void 0, function () {
|
|
78
|
+
var numRetries, method, stringifiedParams, query, url, ignoreSSLIssues, headers, data, response, err_1;
|
|
79
|
+
return __generator(this, function (_a) {
|
|
80
|
+
switch (_a.label) {
|
|
81
|
+
case 0:
|
|
82
|
+
numRetries = (opts.numRetries ? opts.numRetries : 0);
|
|
83
|
+
method = (opts.method || 'GET');
|
|
84
|
+
stringifiedParams = qs_1.default.stringify(opts.params || {}, {
|
|
85
|
+
encodeValuesOnly: true,
|
|
86
|
+
arrayFormat: 'brackets',
|
|
87
|
+
});
|
|
88
|
+
query = (method === 'GET' ? "?".concat(stringifiedParams) : '');
|
|
89
|
+
if (!opts.host) {
|
|
90
|
+
// No host included at all. Just send to a path
|
|
91
|
+
url = "".concat(opts.path).concat(query);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
url = "https://".concat(opts.host).concat(opts.path).concat(query);
|
|
95
|
+
}
|
|
96
|
+
ignoreSSLIssues = (opts.ignoreSSLIssues !== undefined
|
|
97
|
+
? opts.ignoreSSLIssues
|
|
98
|
+
: opts.host === 'localhost:8088');
|
|
99
|
+
headers = opts.headers || {};
|
|
100
|
+
data = null;
|
|
101
|
+
if (!headers['Content-Type']) {
|
|
102
|
+
// Form encoded
|
|
103
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
104
|
+
// Add data if applicable
|
|
105
|
+
data = (method !== 'GET' ? stringifiedParams : null);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// JSON encode
|
|
109
|
+
data = opts.params;
|
|
110
|
+
}
|
|
111
|
+
_a.label = 1;
|
|
112
|
+
case 1:
|
|
113
|
+
_a.trys.push([1, 3, , 4]);
|
|
114
|
+
return [4 /*yield*/, (0, axios_1.default)({
|
|
115
|
+
method: method,
|
|
116
|
+
url: url,
|
|
117
|
+
data: data,
|
|
118
|
+
headers: headers,
|
|
119
|
+
withCredentials: sendCrossDomainCredentials,
|
|
120
|
+
})];
|
|
121
|
+
case 2:
|
|
122
|
+
response = _a.sent();
|
|
123
|
+
// Process response
|
|
124
|
+
return [2 /*return*/, {
|
|
125
|
+
body: response.data,
|
|
126
|
+
status: response.status,
|
|
127
|
+
headers: response.headers,
|
|
128
|
+
}];
|
|
129
|
+
case 3:
|
|
130
|
+
err_1 = _a.sent();
|
|
131
|
+
// Axios throws an error if the request status indicates an error
|
|
132
|
+
// sendRequest is supposed to resolve if the request went through, whether
|
|
133
|
+
// the status indicates an error or not.
|
|
134
|
+
if (err_1.response) {
|
|
135
|
+
// Resolve with response
|
|
136
|
+
return [2 /*return*/, {
|
|
137
|
+
body: err_1.response.data,
|
|
138
|
+
status: err_1.response.status,
|
|
139
|
+
headers: err_1.response.headers,
|
|
140
|
+
}];
|
|
141
|
+
}
|
|
142
|
+
// Request failed! Check if we have more attempts
|
|
143
|
+
if (numRetries > 0) {
|
|
144
|
+
// Update opts with one less retry
|
|
145
|
+
return [2 /*return*/, sendRequest(__assign(__assign({}, opts), { numRetries: opts.numRetries - 1 }))];
|
|
146
|
+
}
|
|
147
|
+
// Self-signed certificate error:
|
|
148
|
+
if (err_1.message.includes('self signed certificate')) {
|
|
149
|
+
throw new caccl_error_1.default({
|
|
150
|
+
message: 'We refused to send a request because the receiver has self-signed certificates.',
|
|
151
|
+
code: ErrorCode_1.default.SelfSigned,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// No tries left
|
|
155
|
+
throw new caccl_error_1.default({
|
|
156
|
+
message: 'We encountered an error when trying to send a network request. If this issue persists, contact an admin.',
|
|
157
|
+
code: ErrorCode_1.default.NotConnected,
|
|
158
|
+
});
|
|
159
|
+
case 4: return [2 /*return*/];
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}); };
|
|
163
|
+
exports.default = sendRequest;
|
|
164
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "caccl-send-request",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.15",
|
|
4
4
|
"description": "The default request sender used throughout the CACCL project.",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"types": "./lib/index.d.ts",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
+
"build": "tsc --project ./tsconfig.json"
|
|
8
9
|
},
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|
|
@@ -18,23 +19,26 @@
|
|
|
18
19
|
"request",
|
|
19
20
|
"CACCL"
|
|
20
21
|
],
|
|
21
|
-
"author": "
|
|
22
|
+
"author": "Gabe Abrams <gabeabrams@gmail.com>",
|
|
22
23
|
"license": "MIT",
|
|
23
24
|
"bugs": {
|
|
24
25
|
"url": "https://github.com/harvard-edtech/caccl-send-request/issues"
|
|
25
26
|
},
|
|
26
27
|
"homepage": "https://github.com/harvard-edtech/caccl-send-request#readme",
|
|
27
28
|
"devDependencies": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
29
|
+
"@types/browserify": "^12.0.37",
|
|
30
|
+
"@types/node": "^17.0.17",
|
|
31
|
+
"@types/qs": "^6.9.7",
|
|
32
|
+
"eslint": "^8.9.0",
|
|
33
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
30
34
|
"eslint-plugin-import": "^2.22.1",
|
|
31
35
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
|
32
|
-
"eslint-plugin-react": "^7.22.0"
|
|
36
|
+
"eslint-plugin-react": "^7.22.0",
|
|
37
|
+
"typescript": "^4.5.5"
|
|
33
38
|
},
|
|
34
39
|
"dependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"https": "^1.0.0",
|
|
40
|
+
"caccl-error": "^2.0.0-beta.15",
|
|
41
|
+
"axios": "^0.21.4",
|
|
38
42
|
"qs": "^6.9.6"
|
|
39
43
|
}
|
|
40
44
|
}
|
package/src/ErrorCode.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Import libs
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import qs from 'qs';
|
|
4
|
+
|
|
5
|
+
// Import other CACCL libs
|
|
6
|
+
import CACCLError from 'caccl-error';
|
|
7
|
+
|
|
8
|
+
// Import shared types
|
|
9
|
+
import ErrorCode from './ErrorCode';
|
|
10
|
+
|
|
11
|
+
// Check if we should send cross-domain credentials
|
|
12
|
+
const sendCrossDomainCredentials = (process.env.NODE_ENV === 'development');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Sends and retries an http request
|
|
16
|
+
* @author Gabriel Abrams
|
|
17
|
+
* @param opts object containing all arguments
|
|
18
|
+
* @param opts.path path to send request to
|
|
19
|
+
* @param [opts.host] host to send request to
|
|
20
|
+
* @param [opts.method=GET] http method to use
|
|
21
|
+
* @param [opts.params] body/data to include in the request
|
|
22
|
+
* @param [opts.headers] headers to include in the request
|
|
23
|
+
* @param [opts.numRetries=0] number of times to retry the request if it
|
|
24
|
+
* fails
|
|
25
|
+
* @param [opts.ignoreSSLIssues] if true, ignores SSL certificate
|
|
26
|
+
* issues. If host is localhost:8088, this will default to true
|
|
27
|
+
* @returns { body, status, headers } on success
|
|
28
|
+
*/
|
|
29
|
+
const sendRequest = async (
|
|
30
|
+
opts: {
|
|
31
|
+
path: string,
|
|
32
|
+
host?: string,
|
|
33
|
+
method?: ('GET' | 'POST' | 'PUT' | 'DELETE'),
|
|
34
|
+
params?: { [k in string]: any },
|
|
35
|
+
headers?: { [k in string]: any },
|
|
36
|
+
numRetries?: number,
|
|
37
|
+
ignoreSSLIssues?: boolean,
|
|
38
|
+
},
|
|
39
|
+
): Promise<{
|
|
40
|
+
body: any,
|
|
41
|
+
status: number,
|
|
42
|
+
headers: { [k in string]: any },
|
|
43
|
+
}> => {
|
|
44
|
+
// Set max number of retries if not defined
|
|
45
|
+
const numRetries = (opts.numRetries ? opts.numRetries : 0);
|
|
46
|
+
|
|
47
|
+
// Process method
|
|
48
|
+
const method: ('GET' | 'POST' | 'PUT' | 'DELETE') = (opts.method || 'GET');
|
|
49
|
+
|
|
50
|
+
// Stringify parameters
|
|
51
|
+
const stringifiedParams = qs.stringify(opts.params || {}, {
|
|
52
|
+
encodeValuesOnly: true,
|
|
53
|
+
arrayFormat: 'brackets',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Create url (include query if GET)
|
|
57
|
+
const query = (method === 'GET' ? `?${stringifiedParams}` : '');
|
|
58
|
+
let url;
|
|
59
|
+
if (!opts.host) {
|
|
60
|
+
// No host included at all. Just send to a path
|
|
61
|
+
url = `${opts.path}${query}`;
|
|
62
|
+
} else {
|
|
63
|
+
url = `https://${opts.host}${opts.path}${query}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Default ignoreSSLIssues
|
|
67
|
+
const ignoreSSLIssues = (
|
|
68
|
+
opts.ignoreSSLIssues !== undefined
|
|
69
|
+
? opts.ignoreSSLIssues
|
|
70
|
+
: opts.host === 'localhost:8088'
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Update headers
|
|
74
|
+
const headers = opts.headers || {};
|
|
75
|
+
let data = null;
|
|
76
|
+
if (!headers['Content-Type']) {
|
|
77
|
+
// Form encoded
|
|
78
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
79
|
+
// Add data if applicable
|
|
80
|
+
data = (method !== 'GET' ? stringifiedParams : null);
|
|
81
|
+
} else {
|
|
82
|
+
// JSON encode
|
|
83
|
+
data = opts.params;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Send request
|
|
87
|
+
try {
|
|
88
|
+
const response = await axios({
|
|
89
|
+
method,
|
|
90
|
+
url,
|
|
91
|
+
data,
|
|
92
|
+
headers,
|
|
93
|
+
withCredentials: sendCrossDomainCredentials,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Process response
|
|
97
|
+
return {
|
|
98
|
+
body: response.data,
|
|
99
|
+
status: response.status,
|
|
100
|
+
headers: response.headers,
|
|
101
|
+
};
|
|
102
|
+
} catch (err) {
|
|
103
|
+
// Axios throws an error if the request status indicates an error
|
|
104
|
+
// sendRequest is supposed to resolve if the request went through, whether
|
|
105
|
+
// the status indicates an error or not.
|
|
106
|
+
if (err.response) {
|
|
107
|
+
// Resolve with response
|
|
108
|
+
return {
|
|
109
|
+
body: err.response.data,
|
|
110
|
+
status: err.response.status,
|
|
111
|
+
headers: err.response.headers,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Request failed! Check if we have more attempts
|
|
116
|
+
if (numRetries > 0) {
|
|
117
|
+
// Update opts with one less retry
|
|
118
|
+
return sendRequest({
|
|
119
|
+
...opts,
|
|
120
|
+
numRetries: opts.numRetries - 1,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Self-signed certificate error:
|
|
125
|
+
if (err.message.includes('self signed certificate')) {
|
|
126
|
+
throw new CACCLError({
|
|
127
|
+
message: 'We refused to send a request because the receiver has self-signed certificates.',
|
|
128
|
+
code: ErrorCode.SelfSigned,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// No tries left
|
|
133
|
+
throw new CACCLError({
|
|
134
|
+
message: 'We encountered an error when trying to send a network request. If this issue persists, contact an admin.',
|
|
135
|
+
code: ErrorCode.NotConnected,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export default sendRequest;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"noImplicitAny": true,
|
|
6
|
+
"noEmitOnError": true,
|
|
7
|
+
"removeComments": false,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"target": "es5",
|
|
11
|
+
"outDir": "./lib"
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"./src"
|
|
15
|
+
],
|
|
16
|
+
"ts-node": {
|
|
17
|
+
"files": true
|
|
18
|
+
}
|
|
19
|
+
}
|
package/errorCodes.js
DELETED
package/index.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
|
-
const qs = require('qs');
|
|
3
|
-
const CACCLError = require('caccl-error');
|
|
4
|
-
const https = require('https');
|
|
5
|
-
|
|
6
|
-
const errorCodes = require('./errorCodes');
|
|
7
|
-
|
|
8
|
-
// Create an agent to ignore unauthorize ssl issues
|
|
9
|
-
const ignoreSSLIssuesAgent = new https.Agent({ rejectUnauthorized: false });
|
|
10
|
-
|
|
11
|
-
// Check if we should send cross-domain credentials
|
|
12
|
-
const sendCrossDomainCredentials = !!(
|
|
13
|
-
process.env.DEV
|
|
14
|
-
|| process.env.NODE_ENV === 'development'
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Sends and retries an http request
|
|
19
|
-
* @author Gabriel Abrams
|
|
20
|
-
* @param {string} host - host to send request to
|
|
21
|
-
* @param {string} path - path to send request to
|
|
22
|
-
* @param {string} [method=GET] - http method to use
|
|
23
|
-
* @param {object} [params] - body/data to include in the request
|
|
24
|
-
* @param {object} [headers] - headers to include in the request
|
|
25
|
-
* @param {number} [numRetries=0] - number of times to retry the request if it
|
|
26
|
-
* fails
|
|
27
|
-
* @param {boolean} [ignoreSSLIssues=false] - if true, ignores SSL certificate
|
|
28
|
-
* issues. If host is localhost:8088, this will default to true
|
|
29
|
-
* @return {Promise.<CACCLErrror|object>} Returns { body, status, headers } on
|
|
30
|
-
* success, CACCLError on failure
|
|
31
|
-
*/
|
|
32
|
-
const sendRequest = (options) => {
|
|
33
|
-
// Set max number of retries if not defined
|
|
34
|
-
const numRetries = (options.numRetries ? options.numRetries : 0);
|
|
35
|
-
|
|
36
|
-
// Use default method if applicable
|
|
37
|
-
const method = options.method || 'GET';
|
|
38
|
-
|
|
39
|
-
// Stringify parameters
|
|
40
|
-
const stringifiedParams = qs.stringify(options.params || {}, {
|
|
41
|
-
encodeValuesOnly: true,
|
|
42
|
-
arrayFormat: 'brackets',
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Create url (include query if GET)
|
|
46
|
-
const query = (method === 'GET' ? `?${stringifiedParams}` : '');
|
|
47
|
-
let url;
|
|
48
|
-
if (!options.host) {
|
|
49
|
-
// No host included at all. Just send to a path
|
|
50
|
-
url = `${options.path}${query}`;
|
|
51
|
-
} else {
|
|
52
|
-
url = `https://${options.host}${options.path}${query}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Default ignoreSSLIssues
|
|
56
|
-
const ignoreSSLIssues = (
|
|
57
|
-
options.ignoreSSLIssues !== undefined
|
|
58
|
-
? options.ignoreSSLIssues
|
|
59
|
-
: options.host === 'localhost:8088'
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
// Prep to ignore ssl issues
|
|
63
|
-
const httpsAgent = (
|
|
64
|
-
ignoreSSLIssues
|
|
65
|
-
? ignoreSSLIssuesAgent
|
|
66
|
-
: undefined
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
// Update headers
|
|
70
|
-
const headers = options.headers || {};
|
|
71
|
-
let data = null;
|
|
72
|
-
if (!headers['Content-Type']) {
|
|
73
|
-
// Form encoded
|
|
74
|
-
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
75
|
-
// Add data if applicable
|
|
76
|
-
data = (method !== 'GET' ? stringifiedParams : null);
|
|
77
|
-
} else {
|
|
78
|
-
// JSON encode
|
|
79
|
-
data = options.params;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Send request
|
|
83
|
-
return axios({
|
|
84
|
-
method,
|
|
85
|
-
url,
|
|
86
|
-
data,
|
|
87
|
-
httpsAgent,
|
|
88
|
-
headers,
|
|
89
|
-
withCredentials: sendCrossDomainCredentials,
|
|
90
|
-
})
|
|
91
|
-
.catch((err) => {
|
|
92
|
-
// Axios throws an error if the request status indicates an error
|
|
93
|
-
// sendRequest is supposed to resolve if the request went through, whether
|
|
94
|
-
// the status indicates an error or not.
|
|
95
|
-
if (err.response) {
|
|
96
|
-
// Resolve with response
|
|
97
|
-
return Promise.resolve(err.response);
|
|
98
|
-
}
|
|
99
|
-
// Request failed! Check if we have more attempts
|
|
100
|
-
if (numRetries > 0) {
|
|
101
|
-
// Update options with one less retry
|
|
102
|
-
const newOptions = options;
|
|
103
|
-
newOptions.numRetries -= 1;
|
|
104
|
-
return sendRequest(newOptions);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Self-signed certificate error:
|
|
108
|
-
if (err.message.includes('self signed certificate')) {
|
|
109
|
-
throw new CACCLError({
|
|
110
|
-
message: 'We refused to send a request because the receiver has self-signed certificates.',
|
|
111
|
-
code: errorCodes.selfSigned,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// No tries left
|
|
116
|
-
throw new CACCLError({
|
|
117
|
-
message: 'We encountered an error when trying to send a network request. If this issue persists, contact an admin.',
|
|
118
|
-
code: errorCodes.notConnected,
|
|
119
|
-
});
|
|
120
|
-
})
|
|
121
|
-
.then((response) => {
|
|
122
|
-
return {
|
|
123
|
-
body: response.data,
|
|
124
|
-
status: response.status,
|
|
125
|
-
headers: response.headers,
|
|
126
|
-
};
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
module.exports = sendRequest;
|