@mimik/api-helper 0.0.1 → 1.0.1
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 +12 -1
- package/index.js +122 -32
- package/lib/common.js +22 -3
- package/lib/extract-helper.js +5 -5
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -57,13 +57,24 @@ Gets the API file from swaggerhub and store it in the give PATH location.
|
|
|
57
57
|
|
|
58
58
|
- <code>Promise</code> An error is thrown if the apiFilename resolution generates an error or the request to the API provider fails or the file connot be saved.
|
|
59
59
|
|
|
60
|
+
`apiInfo` options has the following format:
|
|
61
|
+
``` javascript
|
|
62
|
+
{
|
|
63
|
+
"provider": "provider of the api file, can be `swaggerhub` or `bitbucket`",
|
|
64
|
+
"apiBasicAuth": {
|
|
65
|
+
"username": "username for bitbucket",
|
|
66
|
+
"password": "password for bitbucket"
|
|
67
|
+
},
|
|
68
|
+
"apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible publically"
|
|
69
|
+
}
|
|
70
|
+
|
|
60
71
|
**Requires**: <code>module:@mimik/request-retry</code>, <code>module:@mimik/response-helper</code>, <code>module:@mimik/sumologic-winston-logger</code>, <code>module:fs</code>, <code>module:js-yaml</code>, <code>module:path</code>
|
|
61
72
|
|
|
62
73
|
| Param | Type | Description |
|
|
63
74
|
| --- | --- | --- |
|
|
64
75
|
| apiFilename | <code>PATH.<string></code> | Name of the file where the API file will be stored. |
|
|
65
76
|
| correlationId | <code>UUID.<string></code> | CorrelationId when logging activites. |
|
|
66
|
-
| options | <code>object</code> | Options associated with the call. Use to pass `metrics` to `rpRetry` and `
|
|
77
|
+
| options | <code>object</code> | Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiInfo` to access the api file in the api provider. |
|
|
67
78
|
|
|
68
79
|
<a name="setupServerFiles"></a>
|
|
69
80
|
|
package/index.js
CHANGED
|
@@ -3,6 +3,8 @@ const fs = require('fs');
|
|
|
3
3
|
const pathLib = require('path');
|
|
4
4
|
const yaml = require('js-yaml');
|
|
5
5
|
const _ = require('lodash');
|
|
6
|
+
const SwaggerClient = require('swagger-client');
|
|
7
|
+
const { Base64 } = require('js-base64');
|
|
6
8
|
|
|
7
9
|
const logger = require('@mimik/sumologic-winston-logger');
|
|
8
10
|
const { getRichError } = require('@mimik/response-helper');
|
|
@@ -28,8 +30,16 @@ const {
|
|
|
28
30
|
CLIENT_CREDENTIALS,
|
|
29
31
|
IMPLICIT,
|
|
30
32
|
POSTFIX,
|
|
31
|
-
|
|
33
|
+
API_PROVIDER_SWAGGERHUB,
|
|
34
|
+
API_PROVIDER_BITBUCKET,
|
|
32
35
|
RESOLVED,
|
|
36
|
+
API_SOURCE,
|
|
37
|
+
SWAGGER,
|
|
38
|
+
BITBUCKET,
|
|
39
|
+
SWAGGERHUB,
|
|
40
|
+
EXTENSION_YML,
|
|
41
|
+
DEFAULT_BITBUCKET_USERNAME,
|
|
42
|
+
DEFAULT_BITBUCKET_PASSWORD,
|
|
33
43
|
} = require('./lib/common');
|
|
34
44
|
|
|
35
45
|
/**
|
|
@@ -117,31 +127,71 @@ const apiSetup = (setup, registeredOperations, securityHandlers, extraFormats, c
|
|
|
117
127
|
* @requires path
|
|
118
128
|
* @param {PATH.<string>} apiFilename - Name of the file where the API file will be stored.
|
|
119
129
|
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
120
|
-
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `
|
|
130
|
+
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiInfo` to access the api file in the api provider.
|
|
121
131
|
* @return {Promise}.
|
|
122
132
|
* &fulfil {object} The API file itself.
|
|
123
133
|
* @throws {Promise} An error is thrown if the apiFilename resolution generates an error or the request to the API provider fails or the file connot be saved.
|
|
134
|
+
*
|
|
135
|
+
* `apiInfo` options has the following format:
|
|
136
|
+
* ``` javascript
|
|
137
|
+
* {
|
|
138
|
+
* "provider": "provider of the api file, can be `swaggerhub` or `bitbucket`",
|
|
139
|
+
* "apiBasicAuth": {
|
|
140
|
+
* "username": "username for bitbucket",
|
|
141
|
+
* "password": "password for bitbucket"
|
|
142
|
+
* },
|
|
143
|
+
* "apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible publically"
|
|
144
|
+
* }
|
|
124
145
|
*/
|
|
125
146
|
const getAPIFile = (apiFilename, correlationId, options) => {
|
|
126
|
-
|
|
147
|
+
const swaggerOptions = (spec) => ({
|
|
148
|
+
spec,
|
|
149
|
+
allowMetaPatches: false,
|
|
150
|
+
skipNormalization: true,
|
|
151
|
+
mode: 'strict',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
logger.info('getting API definition', correlationId);
|
|
155
|
+
let apiDefinition;
|
|
156
|
+
|
|
127
157
|
try {
|
|
128
158
|
if (fs.existsSync(apiFilename)) {
|
|
129
159
|
logger.debug('API file already exists', { apiFilename }, correlationId);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
try { apiDefinition = JSON.parse(apiDefinition); }
|
|
133
|
-
catch (errJSON) {
|
|
134
|
-
try { apiDefinition = yaml.load(apiDefinition); }
|
|
135
|
-
catch (errYaml) {
|
|
136
|
-
return Promise.reject(getRichError('System', 'wrong file format', { apiFilename }, errYaml));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return Promise.resolve(apiDefinition);
|
|
160
|
+
apiDefinition = fs.readFileSync(apiFilename, 'utf8');
|
|
140
161
|
}
|
|
141
162
|
}
|
|
142
163
|
catch (err) {
|
|
143
164
|
return Promise.reject(getRichError('System', 'file system error', { apiFilename }, err));
|
|
144
165
|
}
|
|
166
|
+
if (apiDefinition) {
|
|
167
|
+
try { apiDefinition = JSON.parse(apiDefinition); }
|
|
168
|
+
catch (err) {
|
|
169
|
+
return Promise.reject(getRichError('System', 'wrong file format', { apiFilename }, err));
|
|
170
|
+
}
|
|
171
|
+
return SwaggerClient.resolve(swaggerOptions(apiDefinition))
|
|
172
|
+
.catch((err) => {
|
|
173
|
+
throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
|
|
174
|
+
})
|
|
175
|
+
.then((apiDefinitionResult) => {
|
|
176
|
+
if (apiDefinitionResult.errors.length !== 0) {
|
|
177
|
+
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
178
|
+
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
179
|
+
}
|
|
180
|
+
try { fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, 2)); }
|
|
181
|
+
catch (err) {
|
|
182
|
+
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
183
|
+
}
|
|
184
|
+
return apiDefinitionResult.spec;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (!options) {
|
|
188
|
+
return Promise.reject(getRichError('Paremater', 'no options', { apiFilename }));
|
|
189
|
+
}
|
|
190
|
+
const { apiInfo } = options;
|
|
191
|
+
|
|
192
|
+
if (!apiInfo) {
|
|
193
|
+
return Promise.reject(getRichError('Parameter', 'no information for swaggerFile', { apiFilename }));
|
|
194
|
+
}
|
|
145
195
|
let fileName;
|
|
146
196
|
let apiDirectory;
|
|
147
197
|
|
|
@@ -156,45 +206,85 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
156
206
|
}
|
|
157
207
|
try {
|
|
158
208
|
if (!fs.existsSync(apiDirectory)) {
|
|
159
|
-
logger.debug('
|
|
209
|
+
logger.debug('creating directory', { apiDirectory }, correlationId);
|
|
160
210
|
fs.mkdirSync(apiDirectory);
|
|
161
211
|
}
|
|
162
212
|
}
|
|
163
213
|
catch (err) {
|
|
164
214
|
return Promise.reject(getRichError('System', 'file system error', { apiDirectory }, err));
|
|
165
215
|
}
|
|
166
|
-
const url = `${API_PROVIDER}/${params[0]}/${params[1]}/${params[2]}?${RESOLVED}`;
|
|
167
|
-
|
|
168
|
-
logger.debug('API file does not exist, retrieving it', { url }, correlationId);
|
|
169
216
|
const opts = {
|
|
170
217
|
method: 'GET',
|
|
171
|
-
url,
|
|
172
218
|
headers: {
|
|
173
219
|
'x-correlation-id': correlationId,
|
|
174
220
|
},
|
|
175
221
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
222
|
+
const provider = apiInfo.provider || BITBUCKET;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
switch (provider) {
|
|
226
|
+
case SWAGGERHUB: {
|
|
227
|
+
opts.url = `${API_PROVIDER_SWAGGERHUB}/${params[0]}/${params[1]}/${params[2]}?${RESOLVED}`;
|
|
228
|
+
if (apiInfo.apiApiKey) opts.headers.Authorization = apiInfo.apiApiKey;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
case BITBUCKET: {
|
|
232
|
+
if (!apiInfo.apiBasicAuth || !apiInfo.apiBasicAuth.username || !apiInfo.apiBasicAuth.password) {
|
|
233
|
+
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
234
|
+
}
|
|
235
|
+
if (apiInfo.apiBasicAuth.username === DEFAULT_BITBUCKET_USERNAME || apiInfo.apiBasicAuth.password === DEFAULT_BITBUCKET_PASSWORD) {
|
|
236
|
+
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
237
|
+
}
|
|
238
|
+
try { opts.headers.Authorization = `Basic ${Base64.encode(`${apiInfo.apiBasicAuth.username}:${apiInfo.apiBasicAuth.password}`)}`; }
|
|
239
|
+
catch (err) {
|
|
240
|
+
throw getRichError('System', 'could not create basicAuth', { apiFilename }, err);
|
|
241
|
+
}
|
|
242
|
+
opts.url = `${API_PROVIDER_BITBUCKET}/${params[0]}/${params[1]}${API_SOURCE}/${params[2]}/${SWAGGER}${EXTENSION_YML}`;
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
default: {
|
|
246
|
+
throw getRichError('Parameter', 'invalid API provider', { provider, apiFilename });
|
|
247
|
+
}
|
|
181
248
|
}
|
|
182
249
|
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
return Promise.reject(err);
|
|
252
|
+
}
|
|
253
|
+
if (options.metrics) {
|
|
254
|
+
opts.metrics = options.metrics;
|
|
255
|
+
opts.metrics.url = opts.url;
|
|
256
|
+
}
|
|
257
|
+
opts.retry = {
|
|
258
|
+
logLevel: {
|
|
259
|
+
response: 'debug',
|
|
260
|
+
responseDetails: 'type',
|
|
261
|
+
request: 'debug',
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
logger.debug('API file does not exist, retrieving it', { url: opts.url }, correlationId);
|
|
183
265
|
return rpRetry(opts)
|
|
184
|
-
.
|
|
185
|
-
|
|
266
|
+
.then((result) => {
|
|
267
|
+
let resultJSON = result;
|
|
186
268
|
|
|
187
|
-
|
|
188
|
-
|
|
269
|
+
if (typeof result !== 'object') resultJSON = yaml.load(result);
|
|
270
|
+
return SwaggerClient.resolve(swaggerOptions(resultJSON));
|
|
189
271
|
})
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
|
|
272
|
+
.catch((err) => {
|
|
273
|
+
if (err.statusCode) {
|
|
274
|
+
throw err;
|
|
275
|
+
}
|
|
276
|
+
throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
|
|
277
|
+
})
|
|
278
|
+
.then((apiDefinitionResult) => {
|
|
279
|
+
if (apiDefinitionResult.errors.length !== 0) {
|
|
280
|
+
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
281
|
+
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
193
282
|
}
|
|
283
|
+
try { fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, 2)); }
|
|
194
284
|
catch (err) {
|
|
195
|
-
throw getRichError('System',
|
|
285
|
+
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
196
286
|
}
|
|
197
|
-
return
|
|
287
|
+
return apiDefinitionResult.spec;
|
|
198
288
|
});
|
|
199
289
|
};
|
|
200
290
|
|
package/lib/common.js
CHANGED
|
@@ -15,9 +15,16 @@ const API_KEY_SECURITY = 'ApiKeySecurity';
|
|
|
15
15
|
const CLIENT_CREDENTIALS = 'clientCredentials';
|
|
16
16
|
const IMPLICIT = 'implicit';
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const API_PROVIDER_SWAGGERHUB = 'https://api.swaggerhub.com/apis';
|
|
19
|
+
const API_PROVIDER_BITBUCKET = 'https://api.bitbucket.org/2.0/repositories';
|
|
20
20
|
const RESOLVED = 'resolved=true';
|
|
21
|
+
const API_SOURCE = '/src';
|
|
22
|
+
const SWAGGER = 'swagger';
|
|
23
|
+
const BITBUCKET = 'bitbucket';
|
|
24
|
+
const SWAGGERHUB = 'swaggerhub';
|
|
25
|
+
const EXTENSION_YML = '.yml';
|
|
26
|
+
const EXTENSION_JSON = '.json';
|
|
27
|
+
const POSTFIX = `${SWAGGER}${EXTENSION_JSON}`;
|
|
21
28
|
|
|
22
29
|
const LOCAL = 'local';
|
|
23
30
|
const SET_ON = 'on';
|
|
@@ -26,6 +33,9 @@ const SECURITY_OFF = 'mock';
|
|
|
26
33
|
|
|
27
34
|
const DEFAULT_FORMATS = ['date', 'time', 'date-time', 'byte', 'uuid', 'uri', 'email', 'ipv4', 'ipv6'];
|
|
28
35
|
|
|
36
|
+
const DEFAULT_BITBUCKET_USERNAME = ' ';
|
|
37
|
+
const DEFAULT_BITBUCKET_PASSWORD = ' ';
|
|
38
|
+
|
|
29
39
|
const AUTHORIZATION = 'authorization';
|
|
30
40
|
const ADMIN = 'admin';
|
|
31
41
|
const SUB_ADMIN = 'subAdmin';
|
|
@@ -56,13 +66,22 @@ module.exports = {
|
|
|
56
66
|
CLIENT_CREDENTIALS,
|
|
57
67
|
IMPLICIT,
|
|
58
68
|
POSTFIX,
|
|
59
|
-
|
|
69
|
+
API_PROVIDER_SWAGGERHUB,
|
|
70
|
+
API_PROVIDER_BITBUCKET,
|
|
71
|
+
SWAGGER,
|
|
72
|
+
BITBUCKET,
|
|
73
|
+
SWAGGERHUB,
|
|
74
|
+
EXTENSION_JSON,
|
|
75
|
+
EXTENSION_YML,
|
|
60
76
|
RESOLVED,
|
|
77
|
+
API_SOURCE,
|
|
61
78
|
LOCAL,
|
|
62
79
|
SET_ON,
|
|
63
80
|
SECURITY_ON,
|
|
64
81
|
SECURITY_OFF,
|
|
65
82
|
DEFAULT_FORMATS,
|
|
83
|
+
DEFAULT_BITBUCKET_USERNAME,
|
|
84
|
+
DEFAULT_BITBUCKET_PASSWORD,
|
|
66
85
|
AUTHORIZATION,
|
|
67
86
|
ADMIN,
|
|
68
87
|
SUB_ADMIN,
|
package/lib/extract-helper.js
CHANGED
|
@@ -33,17 +33,17 @@ const saveProperties = (extractResult, buildDirectory, controllersDirectoryName,
|
|
|
33
33
|
controllers.forEach((controller) => {
|
|
34
34
|
itemToSave = '';
|
|
35
35
|
extractResult[controller].forEach((operationId) => {
|
|
36
|
-
itemToSave
|
|
36
|
+
itemToSave += ` ${operationId},\n`;
|
|
37
37
|
operationIds.push(operationId);
|
|
38
38
|
});
|
|
39
|
-
stringToSave
|
|
39
|
+
stringToSave += `const {\n${itemToSave}} = require('../${controllersDirectoryName}/${controller}');\n`;
|
|
40
40
|
});
|
|
41
|
-
stringToSave
|
|
41
|
+
stringToSave += '\nmodule.exports = {\n';
|
|
42
42
|
itemToSave = '';
|
|
43
43
|
operationIds.forEach((operationId) => {
|
|
44
|
-
itemToSave
|
|
44
|
+
itemToSave += ` ${operationId},\n`;
|
|
45
45
|
});
|
|
46
|
-
stringToSave
|
|
46
|
+
stringToSave += `${itemToSave}};\n`;
|
|
47
47
|
logger.info(`creating ${filename}`, { filename }, correlationId);
|
|
48
48
|
try {
|
|
49
49
|
fs.writeFileSync(filename, stringToSave);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mimik/api-helper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "helper for openAPI backend and mimik service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,20 +31,22 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@mimik/request-helper":"^1.7.8",
|
|
34
|
-
"@mimik/request-retry": "^
|
|
35
|
-
"@mimik/response-helper": "^
|
|
34
|
+
"@mimik/request-retry": "^3.0.0",
|
|
35
|
+
"@mimik/response-helper": "^3.0.0",
|
|
36
36
|
"@mimik/sumologic-winston-logger": "^1.6.15",
|
|
37
|
-
"@mimik/swagger-helper": "^
|
|
37
|
+
"@mimik/swagger-helper": "^4.0.1",
|
|
38
38
|
"ajv-formats": "^3.0.0-rc.0",
|
|
39
|
+
"js-base64": "3.7.5",
|
|
39
40
|
"js-yaml":"^4.1.0",
|
|
40
41
|
"jsonwebtoken": "^9.0.0",
|
|
41
42
|
"lodash": "^4.17.21",
|
|
42
|
-
"openapi-backend": "^5.9.1"
|
|
43
|
+
"openapi-backend": "^5.9.1",
|
|
44
|
+
"swagger-client": "3.19.6"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@mimik/eslint-plugin-dependencies": "^2.4.5",
|
|
46
48
|
"@mimik/eslint-plugin-document-env": "^1.0.5",
|
|
47
|
-
"eslint": "8.
|
|
49
|
+
"eslint": "8.39.0",
|
|
48
50
|
"eslint-config-airbnb": "19.0.4",
|
|
49
51
|
"eslint-plugin-import": "2.27.5",
|
|
50
52
|
"eslint-plugin-jsx-a11y": "6.7.1",
|