@mimik/api-helper 2.0.9 → 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/index.js CHANGED
@@ -30,6 +30,8 @@ import { OpenAPIBackend } from 'openapi-backend';
30
30
  import SwaggerClient from 'swagger-client';
31
31
  import { ajvFormats } from './lib/ajvHelpers.js';
32
32
  import baseHandlers from './lib/baseHandlers.js';
33
+ import compact from 'lodash.compact';
34
+ import difference from 'lodash.difference';
33
35
  import fs from 'fs';
34
36
  import { getRichError } from '@mimik/response-helper';
35
37
  import { load } from 'js-yaml';
@@ -42,7 +44,7 @@ import { securityLib } from './lib/securityHandlers.js';
42
44
  * @module api-helper
43
45
  * @example
44
46
  * import apiHelper from '@mimik/api-helper';
45
- * // or
47
+ * or
46
48
  * import { apiSetup, securityLib, getAPIFile, validateSecuritySchemes, extractProperties, setupServerFiles } from '@mimik/api-helper';
47
49
  */
48
50
  const EMPTY = 0;
@@ -57,11 +59,13 @@ const POSTFIX_INDEX = 3;
57
59
  * Implement the security flows for the API.
58
60
  *
59
61
  * @function securityLib
60
- * @category sync
62
+ * @category async
61
63
  * @requires @mimik/swagger-helper
62
64
  * @requires jsonwebtoken
65
+ * @requires lodash
63
66
  * @param {object} config - Configuration of the service.
64
- * @return {object} An object containing `SystemSecurity`, `AdminSecurity`, `UserSecurity`, and `ApiKeySecurity` handlers.
67
+ * &fulfil {object} The API file itself.
68
+ * @throws {Promise} An error is thrown if the initiatilization failed.
65
69
  *
66
70
  * This function is used to setup the following security handlers for the API:
67
71
  * - `SystemSecurity` - used for the system operations, like /system, /onbehalf
@@ -74,28 +78,33 @@ export { securityLib };
74
78
 
75
79
  /**
76
80
  *
77
- * Setup the API to be used for a service
81
+ * Setup the API to be use for a service
78
82
  *
79
83
  * @function apiSetup
80
84
  * @category async
81
85
  * @requires @mimik/response-helper
82
86
  * @requires @mimik/sumologic-winston-logger
83
- * @requires openapi-backend
84
- * @param {object} setup - Object containing the apiFilename and the existing security schemes in the API definition.
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.
85
93
  * @param {object} registeredOperations - List of the operation to register for the API.
86
94
  * @param {object} securityHandlers - List of the security handlers to add for the service.
87
- * @param {object} extraFormats - list of the formats to add for validating properties.
95
+ * @param {object} extraFormats - list of the formats to add for validatng properties.
88
96
  * @param {object} config - Configuration of the service.
89
- * @param {UUID.<string>} correlationId - CorrelationId when logging activities.
90
- * @return {Promise.<object>} The API file itself.
91
- * @throws {Promise} An error is thrown if the initialization failed.
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.
92
101
  *
93
102
  * The following scheme names are reserved: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `PeerSecurity`, `ApiKeySecurity`.
94
103
  * The following security schemes can be defaulted: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `ApiKeySecurity`.
95
104
  * The secOptions in the options property passed when using `init` allows the following operations:
96
105
  * - introduce a customer security scheme, in this case secOptions contains: { newSecurityScheme: {function}newSecurityHandler },
97
106
  * - disable a security scheme that is defined in the swagger API, in this case secOptions contains: { securitySchemeToDisable: { {boolean}notEnabled: true } },
98
- * - overwrite an existing security scheme, in this case secOptions contains: { securitySchemeToOverwrite: {function}newSecurityHandler }.
107
+ * - overwite an existing security scheme, in this case secOptions contains: { securitySchemeToOverwrite: {function}newSecurityHandler }.
99
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.
100
109
  * If the secOptions contains unused security schemes, an error is generated.
101
110
  *
@@ -133,7 +142,7 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
133
142
  }
134
143
  const appliedSecurities = [];
135
144
  const registerDefault = (securitySchemeName, securityHandler) => {
136
- if (existingSecuritySchemes.includes(securitySchemeName) && (!securityHandlers || !securityHandlers[securitySchemeName])) {
145
+ if (existingSecuritySchemes.includes(securitySchemeName) && (!securityHandlers || (securityHandlers && !securityHandlers[securitySchemeName]))) {
137
146
  api.registerSecurityHandler(securitySchemeName, securityHandler);
138
147
  appliedSecurities.push(securitySchemeName);
139
148
  }
@@ -143,16 +152,16 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
143
152
  registerDefault(ADMIN_SECURITY, AdminSecurity[mode]);
144
153
  registerDefault(USER_SECURITY, UserSecurity[mode]);
145
154
  registerDefault(API_KEY_SECURITY, ApiKeySecurity[mode]);
146
- const remainingSecurities = definedSecuritySchemes.filter(sec => !appliedSecurities.includes(sec));
155
+ const remainingSecurities = difference(definedSecuritySchemes, appliedSecurities);
147
156
 
148
157
  if (securityHandlers) {
149
158
  const securityHandlerNames = Object.keys(securityHandlers);
150
- const unusedSecuritySchemes = securityHandlerNames.filter(sec => !definedSecuritySchemes.includes(sec));
159
+ const unusedSecuritySchemes = difference(securityHandlerNames, definedSecuritySchemes);
151
160
 
152
161
  if (unusedSecuritySchemes.length !== EMPTY) throw getRichError('System', 'unused handlers for security schemes', { unusedSecuritySchemes });
153
162
 
154
163
  remainingSecurities.forEach((securityScheme) => {
155
- if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme]?.notEnabled) {
164
+ if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme].notEnabled) {
156
165
  throw getRichError('System', 'missing handler for security scheme', { securityScheme });
157
166
  }
158
167
  });
@@ -163,72 +172,16 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
163
172
  });
164
173
  }
165
174
  else if (remainingSecurities.length !== EMPTY) throw getRichError('System', 'missing handlers for security schemes', { missingSecuritySchemes: remainingSecurities });
166
- return api.init()
175
+ api.init()
167
176
  .catch((err) => {
168
177
  throw getRichError('System', 'could not initialize the api', { api }, err);
169
- })
170
- .then(() => api);
171
- };
172
-
173
- const swaggerOptions = spec => ({
174
- spec,
175
- allowMetaPatches: false,
176
- skipNormalization: true,
177
- mode: 'strict',
178
- });
179
-
180
- const saveResolvedSpec = (apiDefinitionResult, apiFilename, correlationId) => {
181
- if (apiDefinitionResult.errors.length !== EMPTY) {
182
- logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
183
- throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
184
- }
185
- try {
186
- fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
187
- }
188
- catch (err) {
189
- throw getRichError('System', 'file system error', { apiFilename }, err);
190
- }
191
- return apiDefinitionResult.spec;
192
- };
193
-
194
- const buildProviderRequest = (params, apiInfo, apiFilename) => {
195
- const provider = apiInfo.provider || BITBUCKET;
196
-
197
- switch (provider) {
198
- case SWAGGERHUB: {
199
- const result = {
200
- url: `${API_PROVIDER_SWAGGERHUB}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}/${params[API_VERSION_INDEX]}?${RESOLVED}`,
201
- };
202
-
203
- if (apiInfo.apiApiKey) result.authorization = apiInfo.apiApiKey;
204
- return result;
205
- }
206
- case BITBUCKET: {
207
- if (!apiInfo.apiBasicAuth || !apiInfo.apiBasicAuth.username || !apiInfo.apiBasicAuth.password) {
208
- throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
209
- }
210
- if (apiInfo.apiBasicAuth.username === DEFAULT_BITBUCKET_USERNAME || apiInfo.apiBasicAuth.password === DEFAULT_BITBUCKET_PASSWORD) {
211
- throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
212
- }
213
- try {
214
- return {
215
- url: `${API_PROVIDER_BITBUCKET}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}${API_SOURCE}/${params[API_VERSION_INDEX]}/${SWAGGER}${EXTENSION_YML}`,
216
- authorization: `Basic ${Base64.encode(`${apiInfo.apiBasicAuth.username}:${apiInfo.apiBasicAuth.password}`)}`,
217
- };
218
- }
219
- catch (err) {
220
- throw getRichError('System', 'could not create basicAuth', { apiFilename }, err);
221
- }
222
- }
223
- default: {
224
- throw getRichError('Parameter', 'invalid API provider', { provider, apiFilename });
225
- }
226
- }
178
+ });
179
+ return api;
227
180
  };
228
181
 
229
182
  /**
230
183
  *
231
- * Gets the API file from swaggerhub and store it in the given PATH location.
184
+ * Gets the API file from swaggerhub and store it in the give PATH location.
232
185
  *
233
186
  * @function getAPIFile
234
187
  * @category async
@@ -239,10 +192,11 @@ const buildProviderRequest = (params, apiInfo, apiFilename) => {
239
192
  * @requires js-yaml
240
193
  * @requires path
241
194
  * @param {PATH.<string>} apiFilename - Name of the file where the API file will be stored.
242
- * @param {UUID.<string>} correlationId - CorrelationId when logging activities.
195
+ * @param {UUID.<string>} correlationId - CorrelationId when logging activites.
243
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.
244
- * @return {Promise.<object>} The API file itself.
245
- * @throws {Promise} An error is thrown if the apiFilename resolution generates an error or the request to the API provider fails or the file cannot be saved.
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.
246
200
  *
247
201
  * `apiInfo` options has the following format:
248
202
  * ``` javascript
@@ -252,11 +206,17 @@ const buildProviderRequest = (params, apiInfo, apiFilename) => {
252
206
  * "username": "username for bitbucket",
253
207
  * "password": "password for bitbucket"
254
208
  * },
255
- * "apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible publicly"
209
+ * "apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible publically"
256
210
  * }
257
- * ```
258
211
  */
259
212
  export const getAPIFile = (apiFilename, correlationId, options) => {
213
+ const swaggerOptions = spec => ({
214
+ spec,
215
+ allowMetaPatches: false,
216
+ skipNormalization: true,
217
+ mode: 'strict',
218
+ });
219
+
260
220
  logger.info('getting API definition', correlationId);
261
221
  let apiDefinition;
262
222
 
@@ -278,12 +238,24 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
278
238
  }
279
239
  return SwaggerClient.resolve(swaggerOptions(apiDefinition))
280
240
  .catch((err) => {
281
- throw getRichError('System', 'could not resolve apiDefinition', { apiFilename }, err);
241
+ throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
282
242
  })
283
- .then(result => saveResolvedSpec(result, apiFilename, correlationId));
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
+ });
284
256
  }
285
257
  if (!options) {
286
- return Promise.reject(getRichError('Parameter', 'no options', { apiFilename }));
258
+ return Promise.reject(getRichError('Paremater', 'no options', { apiFilename }));
287
259
  }
288
260
  const { apiInfo } = options;
289
261
 
@@ -315,33 +287,56 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
315
287
  catch (err) {
316
288
  return Promise.reject(getRichError('System', 'file system error', { apiDirectory }, err));
317
289
  }
318
- let providerResult;
319
-
320
- try {
321
- providerResult = buildProviderRequest(params, apiInfo, apiFilename);
322
- }
323
- catch (err) {
324
- return Promise.reject(err);
325
- }
326
290
  const opts = {
327
291
  method: 'GET',
328
- url: providerResult.url,
329
292
  headers: {
330
293
  'x-correlation-id': correlationId,
331
294
  },
332
- retry: {
333
- logLevel: {
334
- response: 'debug',
335
- responseDetails: 'type',
336
- request: 'debug',
337
- },
338
- },
339
295
  };
296
+ const provider = apiInfo.provider || BITBUCKET;
340
297
 
341
- if (providerResult.authorization) opts.headers.Authorization = providerResult.authorization;
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
+ }
342
329
  if (options.metrics) {
343
- opts.metrics = { ...options.metrics, url: opts.url };
330
+ opts.metrics = options.metrics;
331
+ opts.metrics.url = opts.url;
344
332
  }
333
+ opts.retry = {
334
+ logLevel: {
335
+ response: 'debug',
336
+ responseDetails: 'type',
337
+ request: 'debug',
338
+ },
339
+ };
345
340
  logger.debug('API file does not exist, retrieving it', { url: opts.url }, correlationId);
346
341
  return rpRetry(opts)
347
342
  .then((result) => {
@@ -354,9 +349,21 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
354
349
  if (err.statusCode) {
355
350
  throw err;
356
351
  }
357
- throw getRichError('System', 'could not resolve apiDefinition', { apiFilename }, err);
352
+ throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
358
353
  })
359
- .then(result => saveResolvedSpec(result, apiFilename, correlationId));
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
+ });
360
367
  };
361
368
 
362
369
  /**
@@ -368,9 +375,9 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
368
375
  * @requires @mimik/sumologic-winston-logger
369
376
  * @requires @mimik/response-helper
370
377
  * @param {object} apiDefinition - JSON object containing the API definition.
371
- * @param {UUID.<string>} correlationId - CorrelationId when logging activities.
372
- * @return {Array.<string>} An array of the known securitySchemes that are in the API definition.
373
- * @throws An error is thrown if a validation fails.
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.
374
381
  */
375
382
  export const validateSecuritySchemes = (apiDefinition, correlationId) => {
376
383
  const existingSecuritySchemes = [];
@@ -390,7 +397,7 @@ export const validateSecuritySchemes = (apiDefinition, correlationId) => {
390
397
 
391
398
  /**
392
399
  *
393
- * Extracts the properties from API definition and creates a file binding the handler with the controller operations.
400
+ * Extracts the properties from API definiton and creates a file binding the handler with the controller operations.
394
401
  *
395
402
  * @function extractProperties
396
403
  * @category sync
@@ -400,9 +407,9 @@ export const validateSecuritySchemes = (apiDefinition, correlationId) => {
400
407
  * @param {object} apiDefinition - JSON object containing the API definition.
401
408
  * @param {PATH.<string>} controllersDirectory - Directory to find the controller files.
402
409
  * @param {PATH.<string>} buildDirectory - Directory where the register file will be stored.
403
- * @param {UUID.<string>} correlationId - CorrelationId when logging activities.
404
- * @return {void}
405
- * @throws An error is thrown for many reasons, like operationId does not exist in controllers, controller does not exist...
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...
406
413
  */
407
414
  export const extractProperties = (apiDefinition, controllersDirectory, buildDirectory, correlationId) => {
408
415
  const result = {};
@@ -486,14 +493,15 @@ export const extractProperties = (apiDefinition, controllersDirectory, buildDire
486
493
  * @param {PATH.<string>} apiFilename - Name of the file where the API file will be stored.
487
494
  * @param {PATH.<string>} controllersDirectory - Directory to find the controller files.
488
495
  * @param {PATH.<string>} buildDirectory - Directory where the register file will be stored.
489
- * @param {UUID.<string>} correlationId - CorrelationId when logging activities.
490
- * @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey` to access private API.
491
- * @return {Promise.<object>} The API file, the API filename, the existing known security schemes and the defined security schemes.
492
- * @throws {Promise} An error is thrown for many reasons associated with getAPIFile or validateSecuritySchemes or extractProperties.
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.
493
501
  */
494
502
  export const setupServerFiles = (apiFilename, controllersDirectory, buildDirectory, correlationId, options) => getAPIFile(apiFilename, correlationId, options)
495
503
  .then((apiDefinition) => {
496
- const existingSecuritySchemes = validateSecuritySchemes(apiDefinition, correlationId).filter(Boolean);
504
+ const existingSecuritySchemes = compact(validateSecuritySchemes(apiDefinition, correlationId));
497
505
 
498
506
  extractProperties(apiDefinition, controllersDirectory, buildDirectory, correlationId);
499
507
  const schemes = apiDefinition.components?.securitySchemes;
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 = [...DEFAULT_FORMATS];
17
+ const libFormats = DEFAULT_FORMATS;
18
18
  let formats = origFormats;
19
19
 
20
20
  if (!formats) formats = {};
@@ -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).filter(key => key !== 'authorized');
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 not implemented`);
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 || !security.flows[flow]) {
16
+ if (!security.flows[flow]) {
17
17
  throw getRichError('System', 'no flow type available', { securityType, flow });
18
18
  }
19
19
  return securityType;
@@ -19,6 +19,8 @@ import {
19
19
  USER_SECURITY,
20
20
  } from './common.js';
21
21
  import { TOKEN_PARAMS } from '@mimik/swagger-helper';
22
+ import difference from 'lodash.difference';
23
+ import intersection from 'lodash.intersection';
22
24
  import jwt from 'jsonwebtoken';
23
25
 
24
26
  const UNAUTHORIZED_ERROR = 401;
@@ -91,7 +93,7 @@ const checkHeaders = (headers) => {
91
93
 
92
94
  const checkScopes = (tokenScopes, defScopes, definition) => {
93
95
  if (!tokenScopes) {
94
- throw getError('no scope in authorization token', UNAUTHORIZED_ERROR);
96
+ throw new Error('no scope in authorization token');
95
97
  }
96
98
  let claims = [];
97
99
  let onBehalf = false;
@@ -124,10 +126,10 @@ const checkScopes = (tokenScopes, defScopes, definition) => {
124
126
  }
125
127
  const includedClaims = analyzedScope[CLAIMS_INDEX].split(CLAIMS_SEPARATOR);
126
128
  const definitionClaims = Object.keys(includedDefinition);
127
- const claimsIntersects = includedClaims.filter(cla => definitionClaims.includes(cla));
129
+ const claimsIntersects = intersection(includedClaims, definitionClaims);
128
130
 
129
131
  if (claimsIntersects.length !== includedClaims.length) {
130
- throw getError(`incorrect claims included: ${includedClaims.filter(cla => !claimsIntersects.includes(cla))}`, FORBIDDEN_ERROR);
132
+ throw getError(`incorrect claims included: ${difference(includedClaims, claimsIntersects)}`, FORBIDDEN_ERROR);
131
133
  }
132
134
  claims = claims.concat(claimsIntersects);
133
135
  }
package/package.json CHANGED
@@ -1,17 +1,23 @@
1
1
  {
2
2
  "name": "@mimik/api-helper",
3
- "version": "2.0.9",
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": "mocha test/ --recursive",
11
- "test-ci": "c8 --reporter=lcov --reporter=text npm test",
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,37 +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-retry": "^4.0.9",
31
- "@mimik/response-helper": "^4.0.10",
32
- "@mimik/sumologic-winston-logger": "^2.1.13",
33
- "@mimik/swagger-helper": "^5.0.3",
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.8",
36
- "js-yaml": "4.1.1",
37
- "jsonwebtoken": "9.0.3",
38
- "openapi-backend": "5.16.1",
39
- "swagger-client": "3.36.2"
39
+ "js-base64": "3.7.7",
40
+ "js-yaml":"4.1.0",
41
+ "jsonwebtoken": "9.0.2",
42
+ "lodash.compact": "3.0.1",
43
+ "lodash.difference": "4.5.0",
44
+ "lodash.intersection": "4.4.0",
45
+ "openapi-backend": "5.13.0",
46
+ "swagger-client": "3.35.6"
40
47
  },
41
48
  "devDependencies": {
42
49
  "@eslint/js": "9.32.0",
43
50
  "@mimik/eslint-plugin-document-env": "^2.0.8",
44
- "@stylistic/eslint-plugin": "5.9.0",
45
- "c8": "10.1.3",
46
- "chai": "6.2.2",
51
+ "@stylistic/eslint-plugin": "5.2.2",
47
52
  "eslint": "9.32.0",
48
53
  "eslint-plugin-import": "2.32.0",
49
- "esmock": "2.7.3",
50
- "globals": "17.3.0",
51
54
  "husky": "9.1.7",
52
- "jsdoc-to-markdown": "9.1.3",
53
- "mocha": "11.7.5"
55
+ "jsdoc-to-markdown": "9.1.2"
54
56
  }
55
57
  }
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npx mocha:*)",
5
- "Bash(npm test:*)",
6
- "Bash(npx eslint:*)"
7
- ]
8
- }
9
- }
package/.husky/pre-commit DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/sh
2
- npm run commit-ready
package/.husky/pre-push DELETED
@@ -1,2 +0,0 @@
1
- #!/bin/sh
2
- npm run test