@mimik/local 1.6.0 → 4.4.2
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/.eslintrc +15 -3
- package/.husky/pre-commit +4 -0
- package/.husky/pre-push +4 -0
- package/Gulpfile.js +6 -7
- package/README.md +164 -17
- package/configuration/config.js +4 -4
- package/index.js +365 -94
- package/lib/common.js +55 -3
- package/lib/commonExt.js +29 -21
- package/lib/helpers.js +81 -40
- package/lib/rp-axios-wrapper.js +36 -0
- package/lib/tasks.js +225 -94
- package/manual-test/getAPI.js +1 -1
- package/manual-test/retrieve.js +5 -3
- package/manual-test/start-example.json +46 -0
- package/manual-test/startTest-example.json +8 -0
- package/manual-test/test.json +46 -0
- package/manual-test/testMerge.js +66 -0
- package/manual-test/testString.js +7 -0
- package/package.json +28 -20
- package/package.json.bak +0 -57
package/lib/common.js
CHANGED
|
@@ -4,22 +4,40 @@ const mITConfigFile = '../mITConfig.json';
|
|
|
4
4
|
const mIDConfigFile = '../mIDConfig.json';
|
|
5
5
|
const sumoLogFile = '../sumoLog.json';
|
|
6
6
|
const s3LogFile = '../s3Log.json';
|
|
7
|
+
const kinesisLogFile = '../kinesisLog.json';
|
|
8
|
+
const locationFile = '../locationConfig.json';
|
|
9
|
+
const keyFile = '../key.json';
|
|
7
10
|
const customerConfigFile = '../customerConfig.json';
|
|
8
11
|
const APIProvider = 'https://api.swaggerhub.com/apis';
|
|
9
12
|
const swaggerExt = 'swagger.json';
|
|
10
13
|
const defaultDirectory = './api';
|
|
11
14
|
|
|
12
15
|
const AWS_S3 = 'awsS3';
|
|
16
|
+
const AWS_KINESIS = 'awsKinesis';
|
|
13
17
|
const SUMOLOGIC = 'sumologic';
|
|
14
|
-
const ALL = 'all';
|
|
18
|
+
const ALL = 'all'; // legacy support
|
|
19
|
+
const NONE = 'none';
|
|
20
|
+
const ALL_MODES = [AWS_S3, AWS_KINESIS, SUMOLOGIC, ALL, NONE];
|
|
21
|
+
|
|
22
|
+
const SWAGGER_SEP = '_';
|
|
23
|
+
const TEST = 'test';
|
|
24
|
+
|
|
25
|
+
const SYSTEM_NAME = 'System';
|
|
26
|
+
const TOKEN_SERVICE = 'mST';
|
|
27
|
+
const IT_REGISTRY = 'mIT';
|
|
28
|
+
const IDENTITY_SERVICE = 'mID';
|
|
15
29
|
|
|
16
30
|
const testJsonFile = './server-test.json';
|
|
17
31
|
const shellFile = './server-start.sh';
|
|
18
32
|
const startFile = './local/start.json';
|
|
19
33
|
const testStartFile = './local/testStart.json';
|
|
20
34
|
const exampleStartFile = './local/start-example.json';
|
|
35
|
+
const exampleTestStartFile = './local/testStart-example.json';
|
|
36
|
+
|
|
37
|
+
const LITERAL = true;
|
|
38
|
+
const IGNORE = '!';
|
|
21
39
|
|
|
22
|
-
const correlationId =
|
|
40
|
+
const correlationId = `test-local@0/${new Date().toISOString()}`;
|
|
23
41
|
|
|
24
42
|
const DEFAULT_MST = {
|
|
25
43
|
basePath: '/mST/v1',
|
|
@@ -41,6 +59,7 @@ const DUMMY_MST = {
|
|
|
41
59
|
clientSecret: 'timeForSecret',
|
|
42
60
|
},
|
|
43
61
|
};
|
|
62
|
+
const DUMMY_CUSTOMER_CODE = 'dummy-code';
|
|
44
63
|
const DEFAULT_MIT = {
|
|
45
64
|
basePath: '/mIT/v1',
|
|
46
65
|
protocol: 'http:',
|
|
@@ -79,7 +98,6 @@ const DEFAULT_SUMOLOG = {
|
|
|
79
98
|
code: '--- default code ---',
|
|
80
99
|
},
|
|
81
100
|
};
|
|
82
|
-
|
|
83
101
|
const DEFAULT_S3LOG = {
|
|
84
102
|
region: '--- default region ---',
|
|
85
103
|
accessKeyId: '--- default accessKey Id',
|
|
@@ -89,6 +107,21 @@ const DEFAULT_S3LOG = {
|
|
|
89
107
|
timeout: 1,
|
|
90
108
|
maxSize: 5,
|
|
91
109
|
};
|
|
110
|
+
const DEFAULT_KINESISLOG = {
|
|
111
|
+
region: '--- default region ---',
|
|
112
|
+
accessKeyId: '--- default accessKey Id',
|
|
113
|
+
secretAccessKey: '--- default secret accessKey ---',
|
|
114
|
+
streamNameInfo: '--- default streamName Info ---',
|
|
115
|
+
streamNameError: '--- default streamName Error ---',
|
|
116
|
+
streamNameOther: '--- default streamName Other ---',
|
|
117
|
+
};
|
|
118
|
+
const DEFAULT_KEY = {
|
|
119
|
+
apiKey: null,
|
|
120
|
+
};
|
|
121
|
+
const DEFAULT_LOCATION = {
|
|
122
|
+
url: null,
|
|
123
|
+
key: null,
|
|
124
|
+
};
|
|
92
125
|
|
|
93
126
|
const ERR_CHECK = '✘';
|
|
94
127
|
const SUCCESS_CHECK = '✔︎';
|
|
@@ -100,6 +133,9 @@ module.exports = {
|
|
|
100
133
|
mIDConfigFile,
|
|
101
134
|
sumoLogFile,
|
|
102
135
|
s3LogFile,
|
|
136
|
+
kinesisLogFile,
|
|
137
|
+
locationFile,
|
|
138
|
+
keyFile,
|
|
103
139
|
customerConfigFile,
|
|
104
140
|
APIProvider,
|
|
105
141
|
swaggerExt,
|
|
@@ -109,18 +145,34 @@ module.exports = {
|
|
|
109
145
|
startFile,
|
|
110
146
|
testStartFile,
|
|
111
147
|
exampleStartFile,
|
|
148
|
+
exampleTestStartFile,
|
|
112
149
|
correlationId,
|
|
150
|
+
LITERAL,
|
|
151
|
+
IGNORE,
|
|
152
|
+
TEST,
|
|
113
153
|
DEFAULT_MST,
|
|
114
154
|
DEFAULT_MIT,
|
|
115
155
|
DEFAULT_MID,
|
|
116
156
|
DEFAULT_SUMOLOG,
|
|
117
157
|
DEFAULT_S3LOG,
|
|
158
|
+
DEFAULT_KINESISLOG,
|
|
159
|
+
DEFAULT_KEY,
|
|
160
|
+
DEFAULT_LOCATION,
|
|
118
161
|
DUMMY_MST,
|
|
162
|
+
DUMMY_CUSTOMER_CODE,
|
|
119
163
|
DUMMY_MIT,
|
|
120
164
|
DUMMY_MID,
|
|
121
165
|
ERR_CHECK,
|
|
122
166
|
SUCCESS_CHECK,
|
|
123
167
|
AWS_S3,
|
|
168
|
+
AWS_KINESIS,
|
|
124
169
|
SUMOLOGIC,
|
|
125
170
|
ALL,
|
|
171
|
+
NONE,
|
|
172
|
+
ALL_MODES,
|
|
173
|
+
SWAGGER_SEP,
|
|
174
|
+
SYSTEM_NAME,
|
|
175
|
+
TOKEN_SERVICE,
|
|
176
|
+
IT_REGISTRY,
|
|
177
|
+
IDENTITY_SERVICE,
|
|
126
178
|
};
|
package/lib/commonExt.js
CHANGED
|
@@ -1,38 +1,46 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
|
|
3
3
|
// duplicate of functions in mST lib/common.js
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const ALL_TYPE = 'all';
|
|
5
|
+
const CLUSTER_TYPE = 'cluster';
|
|
6
6
|
|
|
7
7
|
const inList = (type, list) => _.findIndex(list, (listItem) => listItem.type === type);
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
const
|
|
9
|
+
const setServiceInfo = (sharedWith, type, scopes, customer) => {
|
|
10
|
+
const result = { type };
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
return types;
|
|
12
|
+
if (customer) result.customer = customer;
|
|
13
|
+
if (sharedWith) result.sharedWith = sharedWith;
|
|
14
|
+
if (scopes) result.scopes = scopes;
|
|
15
|
+
return result;
|
|
19
16
|
};
|
|
20
17
|
|
|
21
|
-
const
|
|
22
|
-
const index = inList(
|
|
23
|
-
|
|
18
|
+
const getAllTargets = (serviceType, custConfig, serviceIndex) => {
|
|
19
|
+
const index = serviceIndex || serviceIndex === 0 ? serviceIndex : inList(serviceType, custConfig);
|
|
20
|
+
const targets = [];
|
|
21
|
+
const serviceInfo = (target, st) => {
|
|
22
|
+
if (target.type !== CLUSTER_TYPE) return setServiceInfo(null, target.type, target.scopes, target.customer);
|
|
23
|
+
return setServiceInfo(null, st, target.scopes);
|
|
24
|
+
};
|
|
24
25
|
|
|
25
26
|
if (index !== -1) {
|
|
26
|
-
targets
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
custConfig[index].targets.forEach((target) => targets.push(serviceInfo(target, serviceType)));
|
|
28
|
+
const allIndex = inList(ALL_TYPE, custConfig);
|
|
29
|
+
|
|
30
|
+
if (allIndex !== -1) {
|
|
31
|
+
custConfig[allIndex].targets.forEach((allTarget) => {
|
|
32
|
+
if (!allTarget.except || !allTarget.except.includes(serviceType)) {
|
|
33
|
+
const allTargetIndex = inList(allTarget.type === CLUSTER_TYPE ? serviceType : allTarget.type, targets);
|
|
34
|
+
|
|
35
|
+
if (allTargetIndex === -1) targets.push(serviceInfo(allTarget, serviceType));
|
|
36
|
+
else targets[allTargetIndex].scopes = _.union(targets[allTargetIndex].scopes, allTarget.scopes);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
32
40
|
}
|
|
33
41
|
return targets;
|
|
34
42
|
};
|
|
35
43
|
|
|
36
44
|
module.exports = {
|
|
37
|
-
|
|
45
|
+
getAllTargets,
|
|
38
46
|
};
|
package/lib/helpers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable prefer-template, no-console */
|
|
1
|
+
/* eslint-disable prefer-template, no-console, no-process-env, @mimik/document-env/validate-document-env */
|
|
2
2
|
const colors = require('colors');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const ip = require('ip');
|
|
@@ -16,14 +16,14 @@ colors.setTheme({
|
|
|
16
16
|
const exitError = (regType, error, filename) => {
|
|
17
17
|
let details = `{ statusCode: ${error.statusCode || 500}, message: ${error.message || error}`;
|
|
18
18
|
|
|
19
|
-
if (filename) details =
|
|
19
|
+
if (filename) details = `${details}, filename: ${filename}`;
|
|
20
20
|
details = `${details} }`;
|
|
21
21
|
console.error(`${regType}status: ` + 'error'.error + ', ' + details.info);
|
|
22
22
|
process.exit(1);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const start2shell = (start) => {
|
|
26
|
-
let result = '';
|
|
26
|
+
let result = '#!/bin/sh\n';
|
|
27
27
|
|
|
28
28
|
Object.keys(start).forEach((key) => {
|
|
29
29
|
if (key[0] !== '/' && key[1] !== '/') {
|
|
@@ -32,6 +32,7 @@ const start2shell = (start) => {
|
|
|
32
32
|
else result = `${result}export ${key}="${start[key]}"\n`;
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
|
+
result = `${result}node src/index\n`;
|
|
35
36
|
return result;
|
|
36
37
|
};
|
|
37
38
|
|
|
@@ -62,28 +63,18 @@ const start2process = (start) => {
|
|
|
62
63
|
}
|
|
63
64
|
});
|
|
64
65
|
};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (domainName === 'localhost') {
|
|
79
|
-
domainName = `${ip.address()}:${config.port}`;
|
|
80
|
-
}
|
|
81
|
-
else if (net.isIP(domainName)) {
|
|
82
|
-
domainName = `${domainName}:${config.port}`;
|
|
83
|
-
}
|
|
84
|
-
const protocol = config.protocol || 'http:';
|
|
85
|
-
|
|
86
|
-
return `${protocol}//${domainName}${config.basePath}`;
|
|
66
|
+
const setDomainName = (serverType, regType, domainName, port, literal) => {
|
|
67
|
+
if (!domainName) exitError(regType, `domainName must exist in ${serverType}Config.json`);
|
|
68
|
+
const needPort = domainName === 'localhost' || net.isIP(domainName);
|
|
69
|
+
|
|
70
|
+
if (needPort && !port) exitError(regType, `port missing in ${serverType}Config.json`);
|
|
71
|
+
if (domainName === 'localhost' && !literal) return `${ip.address()}:${port}`;
|
|
72
|
+
if (needPort) return `${domainName}:${port}`;
|
|
73
|
+
return domainName;
|
|
74
|
+
};
|
|
75
|
+
const baseUrl = (serverType, regType, config, literal) => {
|
|
76
|
+
if (!config.basePath) exitError(regType, `basePath missing in ${serverType}Config.json`);
|
|
77
|
+
return `${config.protocol || 'http:'}//${setDomainName(serverType, regType, config.domainName, config.port, literal)}${config.basePath}`;
|
|
87
78
|
};
|
|
88
79
|
|
|
89
80
|
/**
|
|
@@ -100,11 +91,13 @@ const baseUrl = (serverType, regType, config) => {
|
|
|
100
91
|
*``` javascript
|
|
101
92
|
* {
|
|
102
93
|
* sourceFilename: {PATH<string>}, // Source filename to use if altFilename does not retrieve any file.
|
|
94
|
+
* sourceFilenameSupp: {PATH<string}, // Source filename supplementatry to by use with source filename.
|
|
103
95
|
* altFilename: {PATH<string>}, // Alternate filename to use if filename does not retrieve any file.
|
|
104
96
|
* default: {object}, // default content if no file exist
|
|
105
97
|
* }
|
|
106
98
|
*````
|
|
107
99
|
* Will exit 1 if there is an error.
|
|
100
|
+
* Source filename supp is only used when source filename exist. If environement variables between source filename and source filename supplementatry exist the environement variable in source filename will be commented out.
|
|
108
101
|
*
|
|
109
102
|
*/
|
|
110
103
|
const retrieve = (regType, filename, options) => {
|
|
@@ -120,22 +113,69 @@ const retrieve = (regType, filename, options) => {
|
|
|
120
113
|
}
|
|
121
114
|
return readFile;
|
|
122
115
|
};
|
|
116
|
+
|
|
117
|
+
const readMerge = (fname, fnameSupp) => {
|
|
118
|
+
const parse = (rawFile, rawFilename) => {
|
|
119
|
+
try { return json.parse(rawFile); }
|
|
120
|
+
catch (errParse) { return exitError(regType, errParse, rawFilename); }
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const merge = (origRawFile, parsedFile, origRawFileSupp, parsedFileSupp) => {
|
|
124
|
+
const keysParsedFile = Object.keys(parsedFile);
|
|
125
|
+
let rawFile = origRawFile;
|
|
126
|
+
let rawFileSupp = origRawFileSupp;
|
|
127
|
+
|
|
128
|
+
Object.keys(parsedFileSupp).forEach((key) => {
|
|
129
|
+
if (keysParsedFile.includes(key)) {
|
|
130
|
+
const regex = new RegExp(`\n[ \t]+"${key}"`);
|
|
131
|
+
|
|
132
|
+
rawFile = rawFile.replace(regex, `\n// "${key}"`);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
rawFile = rawFile.substring(0, rawFile.length - 2);
|
|
136
|
+
rawFileSupp = rawFileSupp.substring(1);
|
|
137
|
+
return `${rawFile},\n//-- test\n${rawFileSupp}`;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
let readFileRaw;
|
|
141
|
+
let readFileSuppRaw;
|
|
142
|
+
|
|
143
|
+
try { readFileRaw = fs.readFileSync(fname).toString(); }
|
|
144
|
+
catch (errFname) {
|
|
145
|
+
if (errFname.code !== 'ENOENT') exitError(regType, errFname, fname);
|
|
146
|
+
throw errFname;
|
|
147
|
+
}
|
|
148
|
+
const readFile = parse(readFileRaw, fname);
|
|
149
|
+
|
|
150
|
+
if (!fnameSupp) return readFile;
|
|
151
|
+
try { readFileSuppRaw = fs.readFileSync(fnameSupp).toString(); }
|
|
152
|
+
catch (errFnameSupp) {
|
|
153
|
+
if (errFnameSupp.code !== 'ENOENT') exitError(regType, errFnameSupp, fnameSupp);
|
|
154
|
+
return readFile;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return parse(merge(readFileRaw, readFile, readFileSuppRaw, parse(readFileSuppRaw, fnameSupp)), `${fname} + ${fnameSupp}`);
|
|
158
|
+
};
|
|
159
|
+
|
|
123
160
|
const write = (fname, content) => {
|
|
124
161
|
try { fs.writeFileSync(fname, json.stringify(content, null, 2)); }
|
|
125
162
|
catch (err) { exitError(regType, err, fname); }
|
|
126
163
|
return content;
|
|
127
164
|
};
|
|
128
|
-
const readSourceDefault = (fsource, content, fname) => {
|
|
129
|
-
let readFile;
|
|
130
165
|
|
|
131
|
-
|
|
166
|
+
const readSourceDefault = (fSource, fSourceSupp, content, fname) => {
|
|
167
|
+
try {
|
|
168
|
+
const readFile = readMerge(fSource, fSourceSupp);
|
|
169
|
+
|
|
170
|
+
if (!fSourceSupp) console.log('- using ' + fSource.warn + ' for ' + fname.info);
|
|
171
|
+
else console.log('- using ' + fSource.warn + ' + ' + fSourceSupp.warn + ' for ' + fname.info);
|
|
172
|
+
return readFile;
|
|
173
|
+
}
|
|
132
174
|
catch (err) {
|
|
133
|
-
if (!content) exitError(regType, { statusCode: 404, message:
|
|
134
|
-
console.log('- using ' + 'default'.warn + ' for ' +
|
|
175
|
+
if (!content) exitError(regType, { statusCode: 404, message: `no files or default (${err.message})` }, fname);
|
|
176
|
+
console.log('- using ' + 'default'.warn + ' for ' + fname.info);
|
|
135
177
|
return content;
|
|
136
178
|
}
|
|
137
|
-
console.log('- using ' + filename.info);
|
|
138
|
-
return readFile;
|
|
139
179
|
};
|
|
140
180
|
|
|
141
181
|
try {
|
|
@@ -144,28 +184,28 @@ const retrieve = (regType, filename, options) => {
|
|
|
144
184
|
return result;
|
|
145
185
|
}
|
|
146
186
|
catch (errFilename) {
|
|
147
|
-
if (!options) exitError(regType, { statusCode: 404, message:
|
|
187
|
+
if (!options) exitError(regType, { statusCode: 404, message: `no options (${errFilename})` }, filename);
|
|
148
188
|
if (options.altFilename) {
|
|
149
189
|
try {
|
|
150
190
|
result = read(options.altFilename);
|
|
151
191
|
console.log('- using ' + options.altFilename.info);
|
|
152
|
-
return result;
|
|
192
|
+
return result;
|
|
153
193
|
}
|
|
154
194
|
catch (errAltFilename) {
|
|
155
|
-
if (options.
|
|
156
|
-
|
|
157
|
-
if (!options.default) exitError(regType, { statusCode: 404, message: 'no files or default' }, filename);
|
|
195
|
+
if (!options.sourceFileName) {
|
|
196
|
+
if (!options.default) exitError(regType, { statusCode: 404, message: `no files or default (${errAltFilename})` }, filename);
|
|
158
197
|
console.log('- using ' + 'default'.warn + ' for ' + filename.info);
|
|
159
|
-
|
|
198
|
+
return write(filename, options.default);
|
|
160
199
|
}
|
|
200
|
+
return write(filename, readSourceDefault(options.sourceFilename, options.sourceFilenameSupp, options.default, filename));
|
|
161
201
|
}
|
|
162
|
-
return write(filename, result);
|
|
163
202
|
}
|
|
164
203
|
if (!options.sourceFilename) {
|
|
204
|
+
if (!options.default) exitError(regType, { statusCode: 404, message: `no files or default (${errFilename.message})` }, filename);
|
|
165
205
|
console.log('- using ' + 'default'.warn + ' for ' + filename.info);
|
|
166
206
|
return write(filename, options.default);
|
|
167
207
|
}
|
|
168
|
-
return write(filename, readSourceDefault(options.sourceFilename, options.default, filename));
|
|
208
|
+
return write(filename, readSourceDefault(options.sourceFilename, options.sourceFilenameSupp, options.default, filename));
|
|
169
209
|
}
|
|
170
210
|
};
|
|
171
211
|
|
|
@@ -175,6 +215,7 @@ module.exports = {
|
|
|
175
215
|
start2env,
|
|
176
216
|
start2process,
|
|
177
217
|
exitError,
|
|
218
|
+
setDomainName,
|
|
178
219
|
baseUrl,
|
|
179
220
|
retrieve,
|
|
180
221
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
|
|
4
|
+
const rp = (options) => axios(options)
|
|
5
|
+
.then((res) => {
|
|
6
|
+
if (options.resolveWithFullResponse) return res;
|
|
7
|
+
return res.data;
|
|
8
|
+
})
|
|
9
|
+
.catch((err) => {
|
|
10
|
+
const { response } = err;
|
|
11
|
+
|
|
12
|
+
if (!response) {
|
|
13
|
+
const error = new Error('system Error');
|
|
14
|
+
|
|
15
|
+
error.statusCode = 500;
|
|
16
|
+
error.title = http.STATUS_CODES[error.statusCode];
|
|
17
|
+
error.info = {
|
|
18
|
+
code: err.code,
|
|
19
|
+
address: err.address,
|
|
20
|
+
port: err.port,
|
|
21
|
+
syscall: err.syscall,
|
|
22
|
+
};
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
const { data } = response;
|
|
26
|
+
const error = new Error(data ? data.message || response.statusText : response.statusText);
|
|
27
|
+
|
|
28
|
+
error.statusCode = response.status;
|
|
29
|
+
error.title = response.statusText;
|
|
30
|
+
if (data && data.info) error.info = data.info;
|
|
31
|
+
throw error;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
rp,
|
|
36
|
+
};
|