@mimik/api-helper 2.0.8 → 2.0.10
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/.nycrc +4 -0
- package/README.md +63 -57
- package/eslint.config.js +11 -30
- package/index.js +117 -113
- package/lib/ajvHelpers.js +1 -1
- package/lib/baseHandlers.js +3 -2
- package/lib/oauthValidation-helper.js +1 -1
- package/lib/securityHandlers.js +1 -1
- package/package.json +23 -24
- package/.claude/settings.local.json +0 -9
- package/.husky/pre-commit +0 -2
- package/.husky/pre-push +0 -2
- package/test/ajvHelpers.test.js +0 -159
- package/test/baseHandlers.test.js +0 -150
- package/test/extract-helper.test.js +0 -100
- package/test/index-async.test.js +0 -599
- package/test/index-sync.test.js +0 -282
- package/test/oauthValidation-helper.test.js +0 -136
- package/test/securityHandlers.test.js +0 -557
package/index.js
CHANGED
|
@@ -44,7 +44,7 @@ import { securityLib } from './lib/securityHandlers.js';
|
|
|
44
44
|
* @module api-helper
|
|
45
45
|
* @example
|
|
46
46
|
* import apiHelper from '@mimik/api-helper';
|
|
47
|
-
*
|
|
47
|
+
* or
|
|
48
48
|
* import { apiSetup, securityLib, getAPIFile, validateSecuritySchemes, extractProperties, setupServerFiles } from '@mimik/api-helper';
|
|
49
49
|
*/
|
|
50
50
|
const EMPTY = 0;
|
|
@@ -59,12 +59,13 @@ const POSTFIX_INDEX = 3;
|
|
|
59
59
|
* Implement the security flows for the API.
|
|
60
60
|
*
|
|
61
61
|
* @function securityLib
|
|
62
|
-
* @category
|
|
62
|
+
* @category async
|
|
63
63
|
* @requires @mimik/swagger-helper
|
|
64
64
|
* @requires jsonwebtoken
|
|
65
65
|
* @requires lodash
|
|
66
66
|
* @param {object} config - Configuration of the service.
|
|
67
|
-
*
|
|
67
|
+
* &fulfil {object} The API file itself.
|
|
68
|
+
* @throws {Promise} An error is thrown if the initiatilization failed.
|
|
68
69
|
*
|
|
69
70
|
* This function is used to setup the following security handlers for the API:
|
|
70
71
|
* - `SystemSecurity` - used for the system operations, like /system, /onbehalf
|
|
@@ -77,29 +78,33 @@ export { securityLib };
|
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
*
|
|
80
|
-
* Setup the API to be
|
|
81
|
+
* Setup the API to be use for a service
|
|
81
82
|
*
|
|
82
83
|
* @function apiSetup
|
|
83
84
|
* @category async
|
|
84
85
|
* @requires @mimik/response-helper
|
|
85
86
|
* @requires @mimik/sumologic-winston-logger
|
|
86
|
-
* @requires
|
|
87
|
-
* @requires
|
|
88
|
-
* @
|
|
87
|
+
* @requires @mimik/swagger-helper
|
|
88
|
+
* @requires ajv-formats
|
|
89
|
+
* @requires fs
|
|
90
|
+
* @requires jsonwebtoken
|
|
91
|
+
* @requires lodash
|
|
92
|
+
* @param {object} setup - Object containing the apiFilename and the exisiting security schemes in the API definition.
|
|
89
93
|
* @param {object} registeredOperations - List of the operation to register for the API.
|
|
90
94
|
* @param {object} securityHandlers - List of the security handlers to add for the service.
|
|
91
|
-
* @param {object} extraFormats - list of the formats to add for
|
|
95
|
+
* @param {object} extraFormats - list of the formats to add for validatng properties.
|
|
92
96
|
* @param {object} config - Configuration of the service.
|
|
93
|
-
* @param {UUID.<string>} correlationId - CorrelationId when logging
|
|
94
|
-
* @return {Promise
|
|
95
|
-
*
|
|
97
|
+
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
98
|
+
* @return {Promise}.
|
|
99
|
+
* &fulfil {object} The API file itself.
|
|
100
|
+
* @throws {Promise} An error is thrown if the initiatilization failed.
|
|
96
101
|
*
|
|
97
102
|
* The following scheme names are reserved: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `PeerSecurity`, `ApiKeySecurity`.
|
|
98
103
|
* The following security schemes can be defaulted: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `ApiKeySecurity`.
|
|
99
104
|
* The secOptions in the options property passed when using `init` allows the following operations:
|
|
100
105
|
* - introduce a customer security scheme, in this case secOptions contains: { newSecurityScheme: {function}newSecurityHandler },
|
|
101
106
|
* - disable a security scheme that is defined in the swagger API, in this case secOptions contains: { securitySchemeToDisable: { {boolean}notEnabled: true } },
|
|
102
|
-
* -
|
|
107
|
+
* - overwite an existing security scheme, in this case secOptions contains: { securitySchemeToOverwrite: {function}newSecurityHandler }.
|
|
103
108
|
* If the secOptions is not present either to introduce, disable or overwrite a security scheme that is present in the swagger API file an error is generated.
|
|
104
109
|
* If the secOptions contains unused security schemes, an error is generated.
|
|
105
110
|
*
|
|
@@ -137,7 +142,7 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
137
142
|
}
|
|
138
143
|
const appliedSecurities = [];
|
|
139
144
|
const registerDefault = (securitySchemeName, securityHandler) => {
|
|
140
|
-
if (existingSecuritySchemes.includes(securitySchemeName) && (!securityHandlers || !securityHandlers[securitySchemeName])) {
|
|
145
|
+
if (existingSecuritySchemes.includes(securitySchemeName) && (!securityHandlers || (securityHandlers && !securityHandlers[securitySchemeName]))) {
|
|
141
146
|
api.registerSecurityHandler(securitySchemeName, securityHandler);
|
|
142
147
|
appliedSecurities.push(securitySchemeName);
|
|
143
148
|
}
|
|
@@ -156,7 +161,7 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
156
161
|
if (unusedSecuritySchemes.length !== EMPTY) throw getRichError('System', 'unused handlers for security schemes', { unusedSecuritySchemes });
|
|
157
162
|
|
|
158
163
|
remainingSecurities.forEach((securityScheme) => {
|
|
159
|
-
if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme]
|
|
164
|
+
if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme].notEnabled) {
|
|
160
165
|
throw getRichError('System', 'missing handler for security scheme', { securityScheme });
|
|
161
166
|
}
|
|
162
167
|
});
|
|
@@ -167,72 +172,16 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
167
172
|
});
|
|
168
173
|
}
|
|
169
174
|
else if (remainingSecurities.length !== EMPTY) throw getRichError('System', 'missing handlers for security schemes', { missingSecuritySchemes: remainingSecurities });
|
|
170
|
-
|
|
175
|
+
api.init()
|
|
171
176
|
.catch((err) => {
|
|
172
177
|
throw getRichError('System', 'could not initialize the api', { api }, err);
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const swaggerOptions = spec => ({
|
|
178
|
-
spec,
|
|
179
|
-
allowMetaPatches: false,
|
|
180
|
-
skipNormalization: true,
|
|
181
|
-
mode: 'strict',
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const saveResolvedSpec = (apiDefinitionResult, apiFilename, correlationId) => {
|
|
185
|
-
if (apiDefinitionResult.errors.length !== EMPTY) {
|
|
186
|
-
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
187
|
-
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
188
|
-
}
|
|
189
|
-
try {
|
|
190
|
-
fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
|
|
191
|
-
}
|
|
192
|
-
catch (err) {
|
|
193
|
-
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
194
|
-
}
|
|
195
|
-
return apiDefinitionResult.spec;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const buildProviderRequest = (params, apiInfo, apiFilename) => {
|
|
199
|
-
const provider = apiInfo.provider || BITBUCKET;
|
|
200
|
-
|
|
201
|
-
switch (provider) {
|
|
202
|
-
case SWAGGERHUB: {
|
|
203
|
-
const result = {
|
|
204
|
-
url: `${API_PROVIDER_SWAGGERHUB}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}/${params[API_VERSION_INDEX]}?${RESOLVED}`,
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
if (apiInfo.apiApiKey) result.authorization = apiInfo.apiApiKey;
|
|
208
|
-
return result;
|
|
209
|
-
}
|
|
210
|
-
case BITBUCKET: {
|
|
211
|
-
if (!apiInfo.apiBasicAuth || !apiInfo.apiBasicAuth.username || !apiInfo.apiBasicAuth.password) {
|
|
212
|
-
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
213
|
-
}
|
|
214
|
-
if (apiInfo.apiBasicAuth.username === DEFAULT_BITBUCKET_USERNAME || apiInfo.apiBasicAuth.password === DEFAULT_BITBUCKET_PASSWORD) {
|
|
215
|
-
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
216
|
-
}
|
|
217
|
-
try {
|
|
218
|
-
return {
|
|
219
|
-
url: `${API_PROVIDER_BITBUCKET}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}${API_SOURCE}/${params[API_VERSION_INDEX]}/${SWAGGER}${EXTENSION_YML}`,
|
|
220
|
-
authorization: `Basic ${Base64.encode(`${apiInfo.apiBasicAuth.username}:${apiInfo.apiBasicAuth.password}`)}`,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
catch (err) {
|
|
224
|
-
throw getRichError('System', 'could not create basicAuth', { apiFilename }, err);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
default: {
|
|
228
|
-
throw getRichError('Parameter', 'invalid API provider', { provider, apiFilename });
|
|
229
|
-
}
|
|
230
|
-
}
|
|
178
|
+
});
|
|
179
|
+
return api;
|
|
231
180
|
};
|
|
232
181
|
|
|
233
182
|
/**
|
|
234
183
|
*
|
|
235
|
-
* Gets the API file from swaggerhub and store it in the
|
|
184
|
+
* Gets the API file from swaggerhub and store it in the give PATH location.
|
|
236
185
|
*
|
|
237
186
|
* @function getAPIFile
|
|
238
187
|
* @category async
|
|
@@ -243,10 +192,11 @@ const buildProviderRequest = (params, apiInfo, apiFilename) => {
|
|
|
243
192
|
* @requires js-yaml
|
|
244
193
|
* @requires path
|
|
245
194
|
* @param {PATH.<string>} apiFilename - Name of the file where the API file will be stored.
|
|
246
|
-
* @param {UUID.<string>} correlationId - CorrelationId when logging
|
|
195
|
+
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
247
196
|
* @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.
|
|
248
|
-
* @return {Promise
|
|
249
|
-
*
|
|
197
|
+
* @return {Promise}.
|
|
198
|
+
* &fulfil {object} The API file itself.
|
|
199
|
+
* @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.
|
|
250
200
|
*
|
|
251
201
|
* `apiInfo` options has the following format:
|
|
252
202
|
* ``` javascript
|
|
@@ -256,11 +206,17 @@ const buildProviderRequest = (params, apiInfo, apiFilename) => {
|
|
|
256
206
|
* "username": "username for bitbucket",
|
|
257
207
|
* "password": "password for bitbucket"
|
|
258
208
|
* },
|
|
259
|
-
* "apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible
|
|
209
|
+
* "apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible publically"
|
|
260
210
|
* }
|
|
261
|
-
* ```
|
|
262
211
|
*/
|
|
263
212
|
export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
213
|
+
const swaggerOptions = spec => ({
|
|
214
|
+
spec,
|
|
215
|
+
allowMetaPatches: false,
|
|
216
|
+
skipNormalization: true,
|
|
217
|
+
mode: 'strict',
|
|
218
|
+
});
|
|
219
|
+
|
|
264
220
|
logger.info('getting API definition', correlationId);
|
|
265
221
|
let apiDefinition;
|
|
266
222
|
|
|
@@ -282,12 +238,24 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
282
238
|
}
|
|
283
239
|
return SwaggerClient.resolve(swaggerOptions(apiDefinition))
|
|
284
240
|
.catch((err) => {
|
|
285
|
-
throw getRichError('System', 'could not resolve
|
|
241
|
+
throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
|
|
286
242
|
})
|
|
287
|
-
.then(
|
|
243
|
+
.then((apiDefinitionResult) => {
|
|
244
|
+
if (apiDefinitionResult.errors.length !== EMPTY) {
|
|
245
|
+
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
246
|
+
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
253
|
+
}
|
|
254
|
+
return apiDefinitionResult.spec;
|
|
255
|
+
});
|
|
288
256
|
}
|
|
289
257
|
if (!options) {
|
|
290
|
-
return Promise.reject(getRichError('
|
|
258
|
+
return Promise.reject(getRichError('Paremater', 'no options', { apiFilename }));
|
|
291
259
|
}
|
|
292
260
|
const { apiInfo } = options;
|
|
293
261
|
|
|
@@ -319,33 +287,56 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
319
287
|
catch (err) {
|
|
320
288
|
return Promise.reject(getRichError('System', 'file system error', { apiDirectory }, err));
|
|
321
289
|
}
|
|
322
|
-
let providerResult;
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
providerResult = buildProviderRequest(params, apiInfo, apiFilename);
|
|
326
|
-
}
|
|
327
|
-
catch (err) {
|
|
328
|
-
return Promise.reject(err);
|
|
329
|
-
}
|
|
330
290
|
const opts = {
|
|
331
291
|
method: 'GET',
|
|
332
|
-
url: providerResult.url,
|
|
333
292
|
headers: {
|
|
334
293
|
'x-correlation-id': correlationId,
|
|
335
294
|
},
|
|
336
|
-
retry: {
|
|
337
|
-
logLevel: {
|
|
338
|
-
response: 'debug',
|
|
339
|
-
responseDetails: 'type',
|
|
340
|
-
request: 'debug',
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
295
|
};
|
|
296
|
+
const provider = apiInfo.provider || BITBUCKET;
|
|
344
297
|
|
|
345
|
-
|
|
298
|
+
try {
|
|
299
|
+
switch (provider) {
|
|
300
|
+
case SWAGGERHUB: {
|
|
301
|
+
opts.url = `${API_PROVIDER_SWAGGERHUB}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}/${params[API_VERSION_INDEX]}?${RESOLVED}`;
|
|
302
|
+
if (apiInfo.apiApiKey) opts.headers.Authorization = apiInfo.apiApiKey;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case BITBUCKET: {
|
|
306
|
+
if (!apiInfo.apiBasicAuth || !apiInfo.apiBasicAuth.username || !apiInfo.apiBasicAuth.password) {
|
|
307
|
+
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
308
|
+
}
|
|
309
|
+
if (apiInfo.apiBasicAuth.username === DEFAULT_BITBUCKET_USERNAME || apiInfo.apiBasicAuth.password === DEFAULT_BITBUCKET_PASSWORD) {
|
|
310
|
+
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
opts.headers.Authorization = `Basic ${Base64.encode(`${apiInfo.apiBasicAuth.username}:${apiInfo.apiBasicAuth.password}`)}`;
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
throw getRichError('System', 'could not create basicAuth', { apiFilename }, err);
|
|
317
|
+
}
|
|
318
|
+
opts.url = `${API_PROVIDER_BITBUCKET}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}${API_SOURCE}/${params[API_VERSION_INDEX]}/${SWAGGER}${EXTENSION_YML}`;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
default: {
|
|
322
|
+
throw getRichError('Parameter', 'invalid API provider', { provider, apiFilename });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
return Promise.reject(err);
|
|
328
|
+
}
|
|
346
329
|
if (options.metrics) {
|
|
347
|
-
opts.metrics =
|
|
330
|
+
opts.metrics = options.metrics;
|
|
331
|
+
opts.metrics.url = opts.url;
|
|
348
332
|
}
|
|
333
|
+
opts.retry = {
|
|
334
|
+
logLevel: {
|
|
335
|
+
response: 'debug',
|
|
336
|
+
responseDetails: 'type',
|
|
337
|
+
request: 'debug',
|
|
338
|
+
},
|
|
339
|
+
};
|
|
349
340
|
logger.debug('API file does not exist, retrieving it', { url: opts.url }, correlationId);
|
|
350
341
|
return rpRetry(opts)
|
|
351
342
|
.then((result) => {
|
|
@@ -358,9 +349,21 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
358
349
|
if (err.statusCode) {
|
|
359
350
|
throw err;
|
|
360
351
|
}
|
|
361
|
-
throw getRichError('System', 'could not resolve
|
|
352
|
+
throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
|
|
362
353
|
})
|
|
363
|
-
.then(
|
|
354
|
+
.then((apiDefinitionResult) => {
|
|
355
|
+
if (apiDefinitionResult.errors.length !== EMPTY) {
|
|
356
|
+
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
357
|
+
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
364
|
+
}
|
|
365
|
+
return apiDefinitionResult.spec;
|
|
366
|
+
});
|
|
364
367
|
};
|
|
365
368
|
|
|
366
369
|
/**
|
|
@@ -372,9 +375,9 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
372
375
|
* @requires @mimik/sumologic-winston-logger
|
|
373
376
|
* @requires @mimik/response-helper
|
|
374
377
|
* @param {object} apiDefinition - JSON object containing the API definition.
|
|
375
|
-
* @param {UUID.<string>} correlationId - CorrelationId when logging
|
|
376
|
-
* @return
|
|
377
|
-
* @throws An error is thrown
|
|
378
|
+
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
379
|
+
* @return An array of the known securitySchemes that are in the API definition.
|
|
380
|
+
* @throws An error is thrown for the first validation fails.
|
|
378
381
|
*/
|
|
379
382
|
export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
380
383
|
const existingSecuritySchemes = [];
|
|
@@ -394,7 +397,7 @@ export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
|
394
397
|
|
|
395
398
|
/**
|
|
396
399
|
*
|
|
397
|
-
* Extracts the properties from API
|
|
400
|
+
* Extracts the properties from API definiton and creates a file binding the handler with the controller operations.
|
|
398
401
|
*
|
|
399
402
|
* @function extractProperties
|
|
400
403
|
* @category sync
|
|
@@ -404,9 +407,9 @@ export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
|
404
407
|
* @param {object} apiDefinition - JSON object containing the API definition.
|
|
405
408
|
* @param {PATH.<string>} controllersDirectory - Directory to find the controller files.
|
|
406
409
|
* @param {PATH.<string>} buildDirectory - Directory where the register file will be stored.
|
|
407
|
-
* @param {UUID.<string>} correlationId - CorrelationId when logging
|
|
408
|
-
* @return
|
|
409
|
-
* @throws An error is thrown for many reasons, like operationId does not exist in controllers, controller
|
|
410
|
+
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
411
|
+
* @return null
|
|
412
|
+
* @throws An error is thrown for many reasons, like operationId does not exist in controllers, controller dies not exist...
|
|
410
413
|
*/
|
|
411
414
|
export const extractProperties = (apiDefinition, controllersDirectory, buildDirectory, correlationId) => {
|
|
412
415
|
const result = {};
|
|
@@ -490,10 +493,11 @@ export const extractProperties = (apiDefinition, controllersDirectory, buildDire
|
|
|
490
493
|
* @param {PATH.<string>} apiFilename - Name of the file where the API file will be stored.
|
|
491
494
|
* @param {PATH.<string>} controllersDirectory - Directory to find the controller files.
|
|
492
495
|
* @param {PATH.<string>} buildDirectory - Directory where the register file will be stored.
|
|
493
|
-
* @param {UUID.<string>} correlationId - CorrelationId when logging
|
|
494
|
-
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey
|
|
495
|
-
* @return {Promise
|
|
496
|
-
*
|
|
496
|
+
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
497
|
+
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey`` to access private API.
|
|
498
|
+
* @return {Promise}.
|
|
499
|
+
* &fulfil {object} The API file, the API filename, the existing known security schemes and the defined security schemes.
|
|
500
|
+
* @throws {Promise} An error is thrown for many reasons assocated with getAPIFile or validateSecuritySchemes or extractProperties.
|
|
497
501
|
*/
|
|
498
502
|
export const setupServerFiles = (apiFilename, controllersDirectory, buildDirectory, correlationId, options) => getAPIFile(apiFilename, correlationId, options)
|
|
499
503
|
.then((apiDefinition) => {
|
package/lib/ajvHelpers.js
CHANGED
|
@@ -14,7 +14,7 @@ const ajvFormats = origFormats => (ajv) => {
|
|
|
14
14
|
validate: /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/u,
|
|
15
15
|
},
|
|
16
16
|
};
|
|
17
|
-
const libFormats =
|
|
17
|
+
const libFormats = DEFAULT_FORMATS;
|
|
18
18
|
let formats = origFormats;
|
|
19
19
|
|
|
20
20
|
if (!formats) formats = {};
|
package/lib/baseHandlers.js
CHANGED
|
@@ -34,8 +34,9 @@ const unauthorizedHandler = (con, req, res) => {
|
|
|
34
34
|
let error = new Error('Unauthorized');
|
|
35
35
|
|
|
36
36
|
error.statusCode = UNAUTHORIZED_ERROR;
|
|
37
|
-
const schemes = Object.keys(con.security)
|
|
37
|
+
const schemes = Object.keys(con.security);
|
|
38
38
|
|
|
39
|
+
delete schemes.authorized;
|
|
39
40
|
schemes.forEach((scheme) => {
|
|
40
41
|
const { error: schemeError } = con.security[scheme] || {};
|
|
41
42
|
if (schemeError) {
|
|
@@ -48,7 +49,7 @@ const unauthorizedHandler = (con, req, res) => {
|
|
|
48
49
|
const notImplemented = (con, req, res) => {
|
|
49
50
|
const { method } = req;
|
|
50
51
|
const path = req.url;
|
|
51
|
-
const error = new Error(`${method} ${path} defined in Swagger specification, but
|
|
52
|
+
const error = new Error(`${req.method} ${path} defined in Swagger specification, but no implemented`);
|
|
52
53
|
|
|
53
54
|
error.statusCode = NOT_IMPLEMENTED_ERROR;
|
|
54
55
|
error.info = {
|
|
@@ -13,7 +13,7 @@ const validateOauth2 = (securitySchemes, securityType, flow) => {
|
|
|
13
13
|
if (security.type !== OAUTH2) {
|
|
14
14
|
throw getRichError('System', `auth type is not ${OAUTH2}`, { securityType, receivedAuth: security.type, expectedAuth: OAUTH2 });
|
|
15
15
|
}
|
|
16
|
-
if (!security.flows
|
|
16
|
+
if (!security.flows[flow]) {
|
|
17
17
|
throw getRichError('System', 'no flow type available', { securityType, flow });
|
|
18
18
|
}
|
|
19
19
|
return securityType;
|
package/lib/securityHandlers.js
CHANGED
|
@@ -93,7 +93,7 @@ const checkHeaders = (headers) => {
|
|
|
93
93
|
|
|
94
94
|
const checkScopes = (tokenScopes, defScopes, definition) => {
|
|
95
95
|
if (!tokenScopes) {
|
|
96
|
-
throw
|
|
96
|
+
throw new Error('no scope in authorization token');
|
|
97
97
|
}
|
|
98
98
|
let claims = [];
|
|
99
99
|
let onBehalf = false;
|
package/package.json
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mimik/api-helper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.10",
|
|
4
4
|
"description": "helper for openAPI backend and mimik service",
|
|
5
5
|
"main": "./index.js",
|
|
6
|
-
"type": "module",
|
|
6
|
+
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "eslint . --no-error-on-unmatched-pattern",
|
|
9
9
|
"docs": "jsdoc2md index.js > README.md",
|
|
10
|
-
"test": "
|
|
11
|
-
"test-ci": "
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 0",
|
|
11
|
+
"test-ci": "echo \"Error: no test specified\" && exit 0",
|
|
12
12
|
"prepublishOnly": "npm run docs && npm run lint && npm run test-ci",
|
|
13
13
|
"commit-ready": "npm run docs && npm run lint && npm run test-ci"
|
|
14
14
|
},
|
|
15
|
+
"husky": {
|
|
16
|
+
"hooks": {
|
|
17
|
+
"pre-commit": "npm run commit-ready",
|
|
18
|
+
"pre-push": "npm run test"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
15
21
|
"keywords": [
|
|
16
22
|
"mimik",
|
|
17
23
|
"microservice",
|
|
@@ -19,40 +25,33 @@
|
|
|
19
25
|
],
|
|
20
26
|
"author": "mimik technology inc <support@mimik.com> (https://developer.mimik.com/)",
|
|
21
27
|
"license": "MIT",
|
|
22
|
-
"engines": {
|
|
23
|
-
"node": ">=24"
|
|
24
|
-
},
|
|
25
28
|
"repository": {
|
|
26
29
|
"type": "git",
|
|
27
30
|
"url": "https://bitbucket.org/mimiktech/api-helper"
|
|
28
31
|
},
|
|
29
32
|
"dependencies": {
|
|
30
|
-
"@mimik/request-
|
|
31
|
-
"@mimik/
|
|
32
|
-
"@mimik/
|
|
33
|
-
"@mimik/
|
|
33
|
+
"@mimik/request-helper":"^2.0.2",
|
|
34
|
+
"@mimik/request-retry": "^4.0.3",
|
|
35
|
+
"@mimik/response-helper": "^4.0.4",
|
|
36
|
+
"@mimik/sumologic-winston-logger": "^2.0.3",
|
|
37
|
+
"@mimik/swagger-helper": "^5.0.2",
|
|
34
38
|
"ajv-formats": "3.0.1",
|
|
35
|
-
"js-base64": "3.7.
|
|
36
|
-
"js-yaml":
|
|
37
|
-
"jsonwebtoken": "9.0.
|
|
38
|
-
"lodash.compact": "3.0.1",
|
|
39
|
+
"js-base64": "3.7.7",
|
|
40
|
+
"js-yaml":"4.1.0",
|
|
41
|
+
"jsonwebtoken": "9.0.2",
|
|
42
|
+
"lodash.compact": "3.0.1",
|
|
39
43
|
"lodash.difference": "4.5.0",
|
|
40
44
|
"lodash.intersection": "4.4.0",
|
|
41
|
-
"openapi-backend": "5.
|
|
42
|
-
"swagger-client": "3.
|
|
45
|
+
"openapi-backend": "5.13.0",
|
|
46
|
+
"swagger-client": "3.35.6"
|
|
43
47
|
},
|
|
44
48
|
"devDependencies": {
|
|
45
49
|
"@eslint/js": "9.32.0",
|
|
46
50
|
"@mimik/eslint-plugin-document-env": "^2.0.8",
|
|
47
|
-
"@stylistic/eslint-plugin": "5.
|
|
48
|
-
"c8": "10.1.3",
|
|
49
|
-
"chai": "6.2.2",
|
|
51
|
+
"@stylistic/eslint-plugin": "5.2.2",
|
|
50
52
|
"eslint": "9.32.0",
|
|
51
53
|
"eslint-plugin-import": "2.32.0",
|
|
52
|
-
"esmock": "2.7.3",
|
|
53
|
-
"globals": "17.3.0",
|
|
54
54
|
"husky": "9.1.7",
|
|
55
|
-
"jsdoc-to-markdown": "9.1.
|
|
56
|
-
"mocha": "11.7.5"
|
|
55
|
+
"jsdoc-to-markdown": "9.1.2"
|
|
57
56
|
}
|
|
58
57
|
}
|
package/.husky/pre-commit
DELETED
package/.husky/pre-push
DELETED