@mimik/api-helper 2.0.10 → 3.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 +93 -69
- package/index.js +129 -129
- package/lib/ajvHelpers.js +1 -1
- package/lib/baseHandlers.js +2 -3
- package/lib/oauthValidation-helper.js +1 -1
- package/lib/securityHandlers.js +3 -5
- package/package.json +24 -25
- package/.nycrc +0 -4
- package/eslint.config.js +0 -63
package/README.md
CHANGED
|
@@ -1,94 +1,86 @@
|
|
|
1
|
+
## Modules
|
|
2
|
+
|
|
3
|
+
<dl>
|
|
4
|
+
<dt><a href="#module_api-helper">api-helper</a></dt>
|
|
5
|
+
<dd></dd>
|
|
6
|
+
</dl>
|
|
7
|
+
|
|
8
|
+
## Typedefs
|
|
9
|
+
|
|
10
|
+
<dl>
|
|
11
|
+
<dt><a href="#UUID">UUID</a> : <code>string</code></dt>
|
|
12
|
+
<dd><p>UUID string in RFC 4122 format.</p>
|
|
13
|
+
</dd>
|
|
14
|
+
<dt><a href="#PATH">PATH</a> : <code>string</code></dt>
|
|
15
|
+
<dd><p>File system path string.</p>
|
|
16
|
+
</dd>
|
|
17
|
+
</dl>
|
|
18
|
+
|
|
1
19
|
<a name="module_api-helper"></a>
|
|
2
20
|
|
|
3
21
|
## api-helper
|
|
4
22
|
**Example**
|
|
5
23
|
```js
|
|
6
24
|
import apiHelper from '@mimik/api-helper';
|
|
7
|
-
or
|
|
25
|
+
// or
|
|
8
26
|
import { apiSetup, securityLib, getAPIFile, validateSecuritySchemes, extractProperties, setupServerFiles } from '@mimik/api-helper';
|
|
9
27
|
```
|
|
10
28
|
|
|
11
29
|
* [api-helper](#module_api-helper)
|
|
12
30
|
* _async_
|
|
13
|
-
* [~
|
|
14
|
-
* [~
|
|
15
|
-
* [~
|
|
16
|
-
* [~setupServerFiles(apiFilename, controllersDirectory, buildDirectory, correlationId, options)](#module_api-helper..setupServerFiles) ⇒ <code>Promise</code>
|
|
31
|
+
* [~apiSetup(setup, registeredOperations, securityHandlers, extraFormats, config, correlationId)](#module_api-helper..apiSetup) ⇒ <code>Promise.<object></code>
|
|
32
|
+
* [~getAPIFile(apiFilename, correlationId, options)](#module_api-helper..getAPIFile) ⇒ <code>Promise.<object></code>
|
|
33
|
+
* [~setupServerFiles(apiFilename, controllersDirectory, buildDirectory, correlationId, options)](#module_api-helper..setupServerFiles) ⇒ <code>Promise.<object></code>
|
|
17
34
|
* _sync_
|
|
18
|
-
* [~
|
|
19
|
-
* [~
|
|
20
|
-
|
|
21
|
-
<a name="module_api-helper..securityLib"></a>
|
|
22
|
-
|
|
23
|
-
### api-helper~securityLib(config)
|
|
24
|
-
Implement the security flows for the API.
|
|
25
|
-
|
|
26
|
-
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
27
|
-
**Category**: async
|
|
28
|
-
**Throws**:
|
|
29
|
-
|
|
30
|
-
- <code>Promise</code> An error is thrown if the initiatilization failed.
|
|
31
|
-
|
|
32
|
-
This function is used to setup the following security handlers for the API:
|
|
33
|
-
- `SystemSecurity` - used for the system operations, like /system, /onbehalf
|
|
34
|
-
- `AdminSecurity` - used for the admin operations, like /admin,
|
|
35
|
-
- `UserSecurity` - used for the user operations, like /user,
|
|
36
|
-
- `ApiKeySecurity` - used for the API key operations, like /apikey,
|
|
37
|
-
The security handlers are used to validate the tokens and scopes for the API operations.
|
|
38
|
-
|
|
39
|
-
**Requires**: <code>module:@mimik/swagger-helper</code>, <code>module:jsonwebtoken</code>, <code>module:lodash</code>
|
|
40
|
-
|
|
41
|
-
| Param | Type | Description |
|
|
42
|
-
| --- | --- | --- |
|
|
43
|
-
| config | <code>object</code> | Configuration of the service. &fulfil {object} The API file itself. |
|
|
35
|
+
* [~securityLib(config)](#module_api-helper..securityLib) ⇒ <code>object</code>
|
|
36
|
+
* [~validateSecuritySchemes(apiDefinition, correlationId)](#module_api-helper..validateSecuritySchemes) ⇒ <code>Array.<string></code>
|
|
37
|
+
* [~extractProperties(apiDefinition, controllersDirectory, buildDirectory, correlationId)](#module_api-helper..extractProperties) ⇒ <code>void</code>
|
|
44
38
|
|
|
45
39
|
<a name="module_api-helper..apiSetup"></a>
|
|
46
40
|
|
|
47
|
-
### api-helper~apiSetup(setup, registeredOperations, securityHandlers, extraFormats, config, correlationId) ⇒ <code>Promise
|
|
48
|
-
Setup the API to be
|
|
41
|
+
### api-helper~apiSetup(setup, registeredOperations, securityHandlers, extraFormats, config, correlationId) ⇒ <code>Promise.<object></code>
|
|
42
|
+
Setup the API to be used for a service
|
|
49
43
|
|
|
50
44
|
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
51
|
-
**Returns**: <code>Promise
|
|
52
|
-
&fulfil {object} The API file itself.
|
|
45
|
+
**Returns**: <code>Promise.<object></code> - The API file itself.
|
|
53
46
|
**Category**: async
|
|
54
47
|
**Throws**:
|
|
55
48
|
|
|
56
|
-
- <code>Promise</code> An error is thrown if the
|
|
49
|
+
- <code>Promise</code> An error is thrown if the initialization failed.
|
|
57
50
|
|
|
58
51
|
The following scheme names are reserved: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `PeerSecurity`, `ApiKeySecurity`.
|
|
59
52
|
The following security schemes can be defaulted: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `ApiKeySecurity`.
|
|
60
53
|
The secOptions in the options property passed when using `init` allows the following operations:
|
|
61
54
|
- introduce a customer security scheme, in this case secOptions contains: { newSecurityScheme: {function}newSecurityHandler },
|
|
62
55
|
- disable a security scheme that is defined in the swagger API, in this case secOptions contains: { securitySchemeToDisable: { {boolean}notEnabled: true } },
|
|
63
|
-
-
|
|
56
|
+
- overwrite an existing security scheme, in this case secOptions contains: { securitySchemeToOverwrite: {function}newSecurityHandler }.
|
|
64
57
|
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.
|
|
65
58
|
If the secOptions contains unused security schemes, an error is generated.
|
|
66
59
|
|
|
67
60
|
The default formats for validation are: `date`, `time`, `date-time`, `byte`, `uuid`, `uri`, `email`, `ipv4`, `ipv6`, `semver`, `ip`.
|
|
68
61
|
|
|
69
|
-
**Requires**: <code>module:@mimik/response-helper</code>, <code>module:@mimik/sumologic-winston-logger</code>, <code>module
|
|
62
|
+
**Requires**: <code>module:@mimik/response-helper</code>, <code>module:@mimik/sumologic-winston-logger</code>, <code>module:openapi-backend</code>
|
|
70
63
|
|
|
71
64
|
| Param | Type | Description |
|
|
72
65
|
| --- | --- | --- |
|
|
73
|
-
| setup | <code>object</code> | Object containing the apiFilename and the
|
|
66
|
+
| setup | <code>object</code> | Object containing the apiFilename and the existing security schemes in the API definition. |
|
|
74
67
|
| registeredOperations | <code>object</code> | List of the operation to register for the API. |
|
|
75
68
|
| securityHandlers | <code>object</code> | List of the security handlers to add for the service. |
|
|
76
|
-
| extraFormats | <code>object</code> | list of the formats to add for
|
|
69
|
+
| extraFormats | <code>object</code> | list of the formats to add for validating properties. |
|
|
77
70
|
| config | <code>object</code> | Configuration of the service. |
|
|
78
|
-
| correlationId | <code>UUID
|
|
71
|
+
| correlationId | [<code>UUID</code>](#UUID) | CorrelationId when logging activities. |
|
|
79
72
|
|
|
80
73
|
<a name="module_api-helper..getAPIFile"></a>
|
|
81
74
|
|
|
82
|
-
### api-helper~getAPIFile(apiFilename, correlationId, options) ⇒ <code>Promise
|
|
83
|
-
Gets the API file from swaggerhub and
|
|
75
|
+
### api-helper~getAPIFile(apiFilename, correlationId, options) ⇒ <code>Promise.<object></code>
|
|
76
|
+
Gets the API file from swaggerhub and stores it in the given PATH location.
|
|
84
77
|
|
|
85
78
|
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
86
|
-
**Returns**: <code>Promise
|
|
87
|
-
&fulfil {object} The API file itself.
|
|
79
|
+
**Returns**: <code>Promise.<object></code> - The API file itself.
|
|
88
80
|
**Category**: async
|
|
89
81
|
**Throws**:
|
|
90
82
|
|
|
91
|
-
- <code>Promise</code> An error is
|
|
83
|
+
- <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 cannot be saved.
|
|
92
84
|
|
|
93
85
|
`apiInfo` options has the following format:
|
|
94
86
|
``` javascript
|
|
@@ -98,77 +90,109 @@ Gets the API file from swaggerhub and store it in the give PATH location.
|
|
|
98
90
|
"username": "username for bitbucket",
|
|
99
91
|
"password": "password for bitbucket"
|
|
100
92
|
},
|
|
101
|
-
"apiApiKey": "apiKey
|
|
93
|
+
"apiApiKey": "apiKey to access private API on swaggerhub, can be optional if the API is accessible publicly"
|
|
102
94
|
}
|
|
95
|
+
```
|
|
103
96
|
|
|
104
97
|
**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>
|
|
105
98
|
|
|
106
99
|
| Param | Type | Description |
|
|
107
100
|
| --- | --- | --- |
|
|
108
|
-
| apiFilename | <code>PATH
|
|
109
|
-
| correlationId | <code>UUID
|
|
101
|
+
| apiFilename | [<code>PATH</code>](#PATH) | Name of the file where the API file will be stored. |
|
|
102
|
+
| correlationId | [<code>UUID</code>](#UUID) | CorrelationId when logging activities. |
|
|
110
103
|
| 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. |
|
|
111
104
|
|
|
112
105
|
<a name="module_api-helper..setupServerFiles"></a>
|
|
113
106
|
|
|
114
|
-
### api-helper~setupServerFiles(apiFilename, controllersDirectory, buildDirectory, correlationId, options) ⇒ <code>Promise
|
|
107
|
+
### api-helper~setupServerFiles(apiFilename, controllersDirectory, buildDirectory, correlationId, options) ⇒ <code>Promise.<object></code>
|
|
115
108
|
Setup and validates files for the server
|
|
116
109
|
|
|
117
110
|
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
118
|
-
**Returns**: <code>Promise
|
|
119
|
-
&fulfil {object} The API file, the API filename, the existing known security schemes and the defined security schemes.
|
|
111
|
+
**Returns**: <code>Promise.<object></code> - The API file, the API filename, the existing known security schemes and the defined security schemes.
|
|
120
112
|
**Category**: async
|
|
121
113
|
**Throws**:
|
|
122
114
|
|
|
123
|
-
- <code>Promise</code> An error is thrown for many reasons
|
|
115
|
+
- <code>Promise</code> An error is thrown for many reasons associated with getAPIFile or validateSecuritySchemes or extractProperties.
|
|
124
116
|
|
|
125
117
|
**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>
|
|
126
118
|
|
|
127
119
|
| Param | Type | Description |
|
|
128
120
|
| --- | --- | --- |
|
|
129
|
-
| apiFilename | <code>PATH
|
|
130
|
-
| controllersDirectory | <code>PATH
|
|
131
|
-
| buildDirectory | <code>PATH
|
|
132
|
-
| correlationId | <code>UUID
|
|
133
|
-
| options | <code>object</code> | Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey
|
|
121
|
+
| apiFilename | [<code>PATH</code>](#PATH) | Name of the file where the API file will be stored. |
|
|
122
|
+
| controllersDirectory | [<code>PATH</code>](#PATH) | Directory to find the controller files. |
|
|
123
|
+
| buildDirectory | [<code>PATH</code>](#PATH) | Directory where the register file will be stored. |
|
|
124
|
+
| correlationId | [<code>UUID</code>](#UUID) | CorrelationId when logging activities. |
|
|
125
|
+
| options | <code>object</code> | Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey` to access private API. |
|
|
126
|
+
|
|
127
|
+
<a name="module_api-helper..securityLib"></a>
|
|
128
|
+
|
|
129
|
+
### api-helper~securityLib(config) ⇒ <code>object</code>
|
|
130
|
+
Implement the security flows for the API.
|
|
131
|
+
|
|
132
|
+
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
133
|
+
**Returns**: <code>object</code> - An object containing `SystemSecurity`, `AdminSecurity`, `UserSecurity`, and `ApiKeySecurity` handlers.
|
|
134
|
+
|
|
135
|
+
This function is used to setup the following security handlers for the API:
|
|
136
|
+
- `SystemSecurity` - used for the system operations, like /system, /onbehalf
|
|
137
|
+
- `AdminSecurity` - used for the admin operations, like /admin,
|
|
138
|
+
- `UserSecurity` - used for the user operations, like /user,
|
|
139
|
+
- `ApiKeySecurity` - used for the API key operations, like /apikey,
|
|
140
|
+
The security handlers are used to validate the tokens and scopes for the API operations.
|
|
141
|
+
**Category**: sync
|
|
142
|
+
**Requires**: <code>module:@mimik/swagger-helper</code>, <code>module:jsonwebtoken</code>
|
|
143
|
+
|
|
144
|
+
| Param | Type | Description |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| config | <code>object</code> | Configuration of the service. |
|
|
134
147
|
|
|
135
148
|
<a name="module_api-helper..validateSecuritySchemes"></a>
|
|
136
149
|
|
|
137
|
-
### api-helper~validateSecuritySchemes(apiDefinition, correlationId) ⇒
|
|
150
|
+
### api-helper~validateSecuritySchemes(apiDefinition, correlationId) ⇒ <code>Array.<string></code>
|
|
138
151
|
Validates the known SecuritySchemes: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `PeerSecurity`, `ApiKeySecurity`.
|
|
139
152
|
|
|
140
153
|
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
141
|
-
**Returns**: An array of the known securitySchemes that are in the API definition.
|
|
154
|
+
**Returns**: <code>Array.<string></code> - An array of the known securitySchemes that are in the API definition.
|
|
142
155
|
**Category**: sync
|
|
143
156
|
**Throws**:
|
|
144
157
|
|
|
145
|
-
- An error is thrown
|
|
158
|
+
- An error is thrown if a validation fails.
|
|
146
159
|
|
|
147
160
|
**Requires**: <code>module:@mimik/sumologic-winston-logger</code>, <code>module:@mimik/response-helper</code>
|
|
148
161
|
|
|
149
162
|
| Param | Type | Description |
|
|
150
163
|
| --- | --- | --- |
|
|
151
164
|
| apiDefinition | <code>object</code> | JSON object containing the API definition. |
|
|
152
|
-
| correlationId | <code>UUID
|
|
165
|
+
| correlationId | [<code>UUID</code>](#UUID) | CorrelationId when logging activities. |
|
|
153
166
|
|
|
154
167
|
<a name="module_api-helper..extractProperties"></a>
|
|
155
168
|
|
|
156
|
-
### api-helper~extractProperties(apiDefinition, controllersDirectory, buildDirectory, correlationId) ⇒
|
|
157
|
-
Extracts the properties from API
|
|
169
|
+
### api-helper~extractProperties(apiDefinition, controllersDirectory, buildDirectory, correlationId) ⇒ <code>void</code>
|
|
170
|
+
Extracts the properties from API definition and creates a file binding the handler with the controller operations.
|
|
158
171
|
|
|
159
172
|
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
160
|
-
**Returns**: null
|
|
161
173
|
**Category**: sync
|
|
162
174
|
**Throws**:
|
|
163
175
|
|
|
164
|
-
- An error is thrown for many reasons, like operationId does not exist in controllers, controller
|
|
176
|
+
- An error is thrown for many reasons, like operationId does not exist in controllers, controller does not exist...
|
|
165
177
|
|
|
166
178
|
**Requires**: <code>module:@mimik/response-helper</code>, <code>module:@mimik/sumologic-winston-logger</code>, <code>module:fs</code>
|
|
167
179
|
|
|
168
180
|
| Param | Type | Description |
|
|
169
181
|
| --- | --- | --- |
|
|
170
182
|
| apiDefinition | <code>object</code> | JSON object containing the API definition. |
|
|
171
|
-
| controllersDirectory | <code>PATH
|
|
172
|
-
| buildDirectory | <code>PATH
|
|
173
|
-
| correlationId | <code>UUID
|
|
183
|
+
| controllersDirectory | [<code>PATH</code>](#PATH) | Directory to find the controller files. |
|
|
184
|
+
| buildDirectory | [<code>PATH</code>](#PATH) | Directory where the register file will be stored. |
|
|
185
|
+
| correlationId | [<code>UUID</code>](#UUID) | CorrelationId when logging activities. |
|
|
186
|
+
|
|
187
|
+
<a name="UUID"></a>
|
|
188
|
+
|
|
189
|
+
## UUID : <code>string</code>
|
|
190
|
+
UUID string in RFC 4122 format.
|
|
191
|
+
|
|
192
|
+
**Kind**: global typedef
|
|
193
|
+
<a name="PATH"></a>
|
|
194
|
+
|
|
195
|
+
## PATH : <code>string</code>
|
|
196
|
+
File system path string.
|
|
174
197
|
|
|
198
|
+
**Kind**: global typedef
|
package/index.js
CHANGED
|
@@ -30,8 +30,6 @@ 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';
|
|
35
33
|
import fs from 'fs';
|
|
36
34
|
import { getRichError } from '@mimik/response-helper';
|
|
37
35
|
import { load } from 'js-yaml';
|
|
@@ -40,11 +38,19 @@ import pathLib from 'path';
|
|
|
40
38
|
import { rpRetry } from '@mimik/request-retry';
|
|
41
39
|
import { saveProperties } from './lib/extract-helper.js';
|
|
42
40
|
import { securityLib } from './lib/securityHandlers.js';
|
|
41
|
+
/**
|
|
42
|
+
* UUID string in RFC 4122 format.
|
|
43
|
+
* @typedef {string} UUID
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* File system path string.
|
|
47
|
+
* @typedef {string} PATH
|
|
48
|
+
*/
|
|
43
49
|
/**
|
|
44
50
|
* @module api-helper
|
|
45
51
|
* @example
|
|
46
52
|
* import apiHelper from '@mimik/api-helper';
|
|
47
|
-
* or
|
|
53
|
+
* // or
|
|
48
54
|
* import { apiSetup, securityLib, getAPIFile, validateSecuritySchemes, extractProperties, setupServerFiles } from '@mimik/api-helper';
|
|
49
55
|
*/
|
|
50
56
|
const EMPTY = 0;
|
|
@@ -59,13 +65,11 @@ const POSTFIX_INDEX = 3;
|
|
|
59
65
|
* Implement the security flows for the API.
|
|
60
66
|
*
|
|
61
67
|
* @function securityLib
|
|
62
|
-
* @category
|
|
68
|
+
* @category sync
|
|
63
69
|
* @requires @mimik/swagger-helper
|
|
64
70
|
* @requires jsonwebtoken
|
|
65
|
-
* @requires lodash
|
|
66
71
|
* @param {object} config - Configuration of the service.
|
|
67
|
-
*
|
|
68
|
-
* @throws {Promise} An error is thrown if the initiatilization failed.
|
|
72
|
+
* @return {object} An object containing `SystemSecurity`, `AdminSecurity`, `UserSecurity`, and `ApiKeySecurity` handlers.
|
|
69
73
|
*
|
|
70
74
|
* This function is used to setup the following security handlers for the API:
|
|
71
75
|
* - `SystemSecurity` - used for the system operations, like /system, /onbehalf
|
|
@@ -78,33 +82,28 @@ export { securityLib };
|
|
|
78
82
|
|
|
79
83
|
/**
|
|
80
84
|
*
|
|
81
|
-
* Setup the API to be
|
|
85
|
+
* Setup the API to be used for a service
|
|
82
86
|
*
|
|
83
87
|
* @function apiSetup
|
|
84
88
|
* @category async
|
|
85
89
|
* @requires @mimik/response-helper
|
|
86
90
|
* @requires @mimik/sumologic-winston-logger
|
|
87
|
-
* @requires
|
|
88
|
-
* @
|
|
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.
|
|
91
|
+
* @requires openapi-backend
|
|
92
|
+
* @param {object} setup - Object containing the apiFilename and the existing security schemes in the API definition.
|
|
93
93
|
* @param {object} registeredOperations - List of the operation to register for the API.
|
|
94
94
|
* @param {object} securityHandlers - List of the security handlers to add for the service.
|
|
95
|
-
* @param {object} extraFormats - list of the formats to add for
|
|
95
|
+
* @param {object} extraFormats - list of the formats to add for validating properties.
|
|
96
96
|
* @param {object} config - Configuration of the service.
|
|
97
|
-
* @param {UUID
|
|
98
|
-
* @return {Promise}.
|
|
99
|
-
*
|
|
100
|
-
* @throws {Promise} An error is thrown if the initiatilization failed.
|
|
97
|
+
* @param {UUID} correlationId - CorrelationId when logging activities.
|
|
98
|
+
* @return {Promise.<object>} The API file itself.
|
|
99
|
+
* @throws {Promise} An error is thrown if the initialization failed.
|
|
101
100
|
*
|
|
102
101
|
* The following scheme names are reserved: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `PeerSecurity`, `ApiKeySecurity`.
|
|
103
102
|
* The following security schemes can be defaulted: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `ApiKeySecurity`.
|
|
104
103
|
* The secOptions in the options property passed when using `init` allows the following operations:
|
|
105
104
|
* - introduce a customer security scheme, in this case secOptions contains: { newSecurityScheme: {function}newSecurityHandler },
|
|
106
105
|
* - disable a security scheme that is defined in the swagger API, in this case secOptions contains: { securitySchemeToDisable: { {boolean}notEnabled: true } },
|
|
107
|
-
* -
|
|
106
|
+
* - overwrite an existing security scheme, in this case secOptions contains: { securitySchemeToOverwrite: {function}newSecurityHandler }.
|
|
108
107
|
* 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.
|
|
109
108
|
* If the secOptions contains unused security schemes, an error is generated.
|
|
110
109
|
*
|
|
@@ -142,7 +141,7 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
142
141
|
}
|
|
143
142
|
const appliedSecurities = [];
|
|
144
143
|
const registerDefault = (securitySchemeName, securityHandler) => {
|
|
145
|
-
if (existingSecuritySchemes.includes(securitySchemeName) && (!securityHandlers ||
|
|
144
|
+
if (existingSecuritySchemes.includes(securitySchemeName) && (!securityHandlers || !securityHandlers[securitySchemeName])) {
|
|
146
145
|
api.registerSecurityHandler(securitySchemeName, securityHandler);
|
|
147
146
|
appliedSecurities.push(securitySchemeName);
|
|
148
147
|
}
|
|
@@ -152,16 +151,16 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
152
151
|
registerDefault(ADMIN_SECURITY, AdminSecurity[mode]);
|
|
153
152
|
registerDefault(USER_SECURITY, UserSecurity[mode]);
|
|
154
153
|
registerDefault(API_KEY_SECURITY, ApiKeySecurity[mode]);
|
|
155
|
-
const remainingSecurities =
|
|
154
|
+
const remainingSecurities = definedSecuritySchemes.filter(sec => !appliedSecurities.includes(sec));
|
|
156
155
|
|
|
157
156
|
if (securityHandlers) {
|
|
158
157
|
const securityHandlerNames = Object.keys(securityHandlers);
|
|
159
|
-
const unusedSecuritySchemes =
|
|
158
|
+
const unusedSecuritySchemes = securityHandlerNames.filter(sec => !definedSecuritySchemes.includes(sec));
|
|
160
159
|
|
|
161
160
|
if (unusedSecuritySchemes.length !== EMPTY) throw getRichError('System', 'unused handlers for security schemes', { unusedSecuritySchemes });
|
|
162
161
|
|
|
163
162
|
remainingSecurities.forEach((securityScheme) => {
|
|
164
|
-
if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme]
|
|
163
|
+
if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme]?.notEnabled) {
|
|
165
164
|
throw getRichError('System', 'missing handler for security scheme', { securityScheme });
|
|
166
165
|
}
|
|
167
166
|
});
|
|
@@ -172,16 +171,72 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
172
171
|
});
|
|
173
172
|
}
|
|
174
173
|
else if (remainingSecurities.length !== EMPTY) throw getRichError('System', 'missing handlers for security schemes', { missingSecuritySchemes: remainingSecurities });
|
|
175
|
-
api.init()
|
|
174
|
+
return api.init()
|
|
176
175
|
.catch((err) => {
|
|
177
176
|
throw getRichError('System', 'could not initialize the api', { api }, err);
|
|
178
|
-
})
|
|
179
|
-
|
|
177
|
+
})
|
|
178
|
+
.then(() => api);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const swaggerOptions = spec => ({
|
|
182
|
+
spec,
|
|
183
|
+
allowMetaPatches: false,
|
|
184
|
+
skipNormalization: true,
|
|
185
|
+
mode: 'strict',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const saveResolvedSpec = (apiDefinitionResult, apiFilename, correlationId) => {
|
|
189
|
+
if (apiDefinitionResult.errors.length !== EMPTY) {
|
|
190
|
+
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
191
|
+
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
198
|
+
}
|
|
199
|
+
return apiDefinitionResult.spec;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const buildProviderRequest = (params, apiInfo, apiFilename) => {
|
|
203
|
+
const provider = apiInfo.provider || BITBUCKET;
|
|
204
|
+
|
|
205
|
+
switch (provider) {
|
|
206
|
+
case SWAGGERHUB: {
|
|
207
|
+
const result = {
|
|
208
|
+
url: `${API_PROVIDER_SWAGGERHUB}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}/${params[API_VERSION_INDEX]}?${RESOLVED}`,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
if (apiInfo.apiApiKey) result.authorization = apiInfo.apiApiKey;
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
case BITBUCKET: {
|
|
215
|
+
if (!apiInfo.apiBasicAuth || !apiInfo.apiBasicAuth.username || !apiInfo.apiBasicAuth.password) {
|
|
216
|
+
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
217
|
+
}
|
|
218
|
+
if (apiInfo.apiBasicAuth.username === DEFAULT_BITBUCKET_USERNAME || apiInfo.apiBasicAuth.password === DEFAULT_BITBUCKET_PASSWORD) {
|
|
219
|
+
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
return {
|
|
223
|
+
url: `${API_PROVIDER_BITBUCKET}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}${API_SOURCE}/${params[API_VERSION_INDEX]}/${SWAGGER}${EXTENSION_YML}`,
|
|
224
|
+
authorization: `Basic ${Base64.encode(`${apiInfo.apiBasicAuth.username}:${apiInfo.apiBasicAuth.password}`)}`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
throw getRichError('System', 'could not create basicAuth', { apiFilename }, err);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
default: {
|
|
232
|
+
throw getRichError('Parameter', 'invalid API provider', { provider, apiFilename });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
180
235
|
};
|
|
181
236
|
|
|
182
237
|
/**
|
|
183
238
|
*
|
|
184
|
-
* Gets the API file from swaggerhub and
|
|
239
|
+
* Gets the API file from swaggerhub and stores it in the given PATH location.
|
|
185
240
|
*
|
|
186
241
|
* @function getAPIFile
|
|
187
242
|
* @category async
|
|
@@ -191,12 +246,11 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
191
246
|
* @requires fs
|
|
192
247
|
* @requires js-yaml
|
|
193
248
|
* @requires path
|
|
194
|
-
* @param {PATH
|
|
195
|
-
* @param {UUID
|
|
249
|
+
* @param {PATH} apiFilename - Name of the file where the API file will be stored.
|
|
250
|
+
* @param {UUID} correlationId - CorrelationId when logging activities.
|
|
196
251
|
* @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.
|
|
197
|
-
* @return {Promise}.
|
|
198
|
-
*
|
|
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.
|
|
252
|
+
* @return {Promise.<object>} The API file itself.
|
|
253
|
+
* @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.
|
|
200
254
|
*
|
|
201
255
|
* `apiInfo` options has the following format:
|
|
202
256
|
* ``` javascript
|
|
@@ -206,17 +260,11 @@ export const apiSetup = (setup, registeredOperations, securityHandlers, extraFor
|
|
|
206
260
|
* "username": "username for bitbucket",
|
|
207
261
|
* "password": "password for bitbucket"
|
|
208
262
|
* },
|
|
209
|
-
* "apiApiKey": "apiKey
|
|
263
|
+
* "apiApiKey": "apiKey to access private API on swaggerhub, can be optional if the API is accessible publicly"
|
|
210
264
|
* }
|
|
265
|
+
* ```
|
|
211
266
|
*/
|
|
212
267
|
export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
213
|
-
const swaggerOptions = spec => ({
|
|
214
|
-
spec,
|
|
215
|
-
allowMetaPatches: false,
|
|
216
|
-
skipNormalization: true,
|
|
217
|
-
mode: 'strict',
|
|
218
|
-
});
|
|
219
|
-
|
|
220
268
|
logger.info('getting API definition', correlationId);
|
|
221
269
|
let apiDefinition;
|
|
222
270
|
|
|
@@ -238,24 +286,12 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
238
286
|
}
|
|
239
287
|
return SwaggerClient.resolve(swaggerOptions(apiDefinition))
|
|
240
288
|
.catch((err) => {
|
|
241
|
-
throw getRichError('System', 'could not resolve
|
|
289
|
+
throw getRichError('System', 'could not resolve apiDefinition', { apiFilename }, err);
|
|
242
290
|
})
|
|
243
|
-
.then(
|
|
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
|
-
});
|
|
291
|
+
.then(result => saveResolvedSpec(result, apiFilename, correlationId));
|
|
256
292
|
}
|
|
257
293
|
if (!options) {
|
|
258
|
-
return Promise.reject(getRichError('
|
|
294
|
+
return Promise.reject(getRichError('Parameter', 'no options', { apiFilename }));
|
|
259
295
|
}
|
|
260
296
|
const { apiInfo } = options;
|
|
261
297
|
|
|
@@ -287,56 +323,33 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
287
323
|
catch (err) {
|
|
288
324
|
return Promise.reject(getRichError('System', 'file system error', { apiDirectory }, err));
|
|
289
325
|
}
|
|
326
|
+
let providerResult;
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
providerResult = buildProviderRequest(params, apiInfo, apiFilename);
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
return Promise.reject(err);
|
|
333
|
+
}
|
|
290
334
|
const opts = {
|
|
291
335
|
method: 'GET',
|
|
336
|
+
url: providerResult.url,
|
|
292
337
|
headers: {
|
|
293
338
|
'x-correlation-id': correlationId,
|
|
294
339
|
},
|
|
340
|
+
retry: {
|
|
341
|
+
logLevel: {
|
|
342
|
+
response: 'debug',
|
|
343
|
+
responseDetails: 'type',
|
|
344
|
+
request: 'debug',
|
|
345
|
+
},
|
|
346
|
+
},
|
|
295
347
|
};
|
|
296
|
-
const provider = apiInfo.provider || BITBUCKET;
|
|
297
348
|
|
|
298
|
-
|
|
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
|
-
}
|
|
349
|
+
if (providerResult.authorization) opts.headers.Authorization = providerResult.authorization;
|
|
329
350
|
if (options.metrics) {
|
|
330
|
-
opts.metrics = options.metrics;
|
|
331
|
-
opts.metrics.url = opts.url;
|
|
351
|
+
opts.metrics = { ...options.metrics, url: opts.url };
|
|
332
352
|
}
|
|
333
|
-
opts.retry = {
|
|
334
|
-
logLevel: {
|
|
335
|
-
response: 'debug',
|
|
336
|
-
responseDetails: 'type',
|
|
337
|
-
request: 'debug',
|
|
338
|
-
},
|
|
339
|
-
};
|
|
340
353
|
logger.debug('API file does not exist, retrieving it', { url: opts.url }, correlationId);
|
|
341
354
|
return rpRetry(opts)
|
|
342
355
|
.then((result) => {
|
|
@@ -349,21 +362,9 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
349
362
|
if (err.statusCode) {
|
|
350
363
|
throw err;
|
|
351
364
|
}
|
|
352
|
-
throw getRichError('System', 'could not resolve
|
|
365
|
+
throw getRichError('System', 'could not resolve apiDefinition', { apiFilename }, err);
|
|
353
366
|
})
|
|
354
|
-
.then(
|
|
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
|
-
});
|
|
367
|
+
.then(result => saveResolvedSpec(result, apiFilename, correlationId));
|
|
367
368
|
};
|
|
368
369
|
|
|
369
370
|
/**
|
|
@@ -375,9 +376,9 @@ export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
375
376
|
* @requires @mimik/sumologic-winston-logger
|
|
376
377
|
* @requires @mimik/response-helper
|
|
377
378
|
* @param {object} apiDefinition - JSON object containing the API definition.
|
|
378
|
-
* @param {UUID
|
|
379
|
-
* @return An array of the known securitySchemes that are in the API definition.
|
|
380
|
-
* @throws An error is thrown
|
|
379
|
+
* @param {UUID} correlationId - CorrelationId when logging activities.
|
|
380
|
+
* @return {Array.<string>} An array of the known securitySchemes that are in the API definition.
|
|
381
|
+
* @throws An error is thrown if a validation fails.
|
|
381
382
|
*/
|
|
382
383
|
export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
383
384
|
const existingSecuritySchemes = [];
|
|
@@ -397,7 +398,7 @@ export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
|
397
398
|
|
|
398
399
|
/**
|
|
399
400
|
*
|
|
400
|
-
* Extracts the properties from API
|
|
401
|
+
* Extracts the properties from API definition and creates a file binding the handler with the controller operations.
|
|
401
402
|
*
|
|
402
403
|
* @function extractProperties
|
|
403
404
|
* @category sync
|
|
@@ -405,11 +406,11 @@ export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
|
405
406
|
* @requires @mimik/sumologic-winston-logger
|
|
406
407
|
* @requires fs
|
|
407
408
|
* @param {object} apiDefinition - JSON object containing the API definition.
|
|
408
|
-
* @param {PATH
|
|
409
|
-
* @param {PATH
|
|
410
|
-
* @param {UUID
|
|
411
|
-
* @return
|
|
412
|
-
* @throws An error is thrown for many reasons, like operationId does not exist in controllers, controller
|
|
409
|
+
* @param {PATH} controllersDirectory - Directory to find the controller files.
|
|
410
|
+
* @param {PATH} buildDirectory - Directory where the register file will be stored.
|
|
411
|
+
* @param {UUID} correlationId - CorrelationId when logging activities.
|
|
412
|
+
* @return {void}
|
|
413
|
+
* @throws An error is thrown for many reasons, like operationId does not exist in controllers, controller does not exist...
|
|
413
414
|
*/
|
|
414
415
|
export const extractProperties = (apiDefinition, controllersDirectory, buildDirectory, correlationId) => {
|
|
415
416
|
const result = {};
|
|
@@ -490,18 +491,17 @@ export const extractProperties = (apiDefinition, controllersDirectory, buildDire
|
|
|
490
491
|
* @requires fs
|
|
491
492
|
* @requires js-yaml
|
|
492
493
|
* @requires path
|
|
493
|
-
* @param {PATH
|
|
494
|
-
* @param {PATH
|
|
495
|
-
* @param {PATH
|
|
496
|
-
* @param {UUID
|
|
497
|
-
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey
|
|
498
|
-
* @return {Promise}.
|
|
499
|
-
*
|
|
500
|
-
* @throws {Promise} An error is thrown for many reasons assocated with getAPIFile or validateSecuritySchemes or extractProperties.
|
|
494
|
+
* @param {PATH} apiFilename - Name of the file where the API file will be stored.
|
|
495
|
+
* @param {PATH} controllersDirectory - Directory to find the controller files.
|
|
496
|
+
* @param {PATH} buildDirectory - Directory where the register file will be stored.
|
|
497
|
+
* @param {UUID} correlationId - CorrelationId when logging activities.
|
|
498
|
+
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey` to access private API.
|
|
499
|
+
* @return {Promise.<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 associated with getAPIFile or validateSecuritySchemes or extractProperties.
|
|
501
501
|
*/
|
|
502
502
|
export const setupServerFiles = (apiFilename, controllersDirectory, buildDirectory, correlationId, options) => getAPIFile(apiFilename, correlationId, options)
|
|
503
503
|
.then((apiDefinition) => {
|
|
504
|
-
const existingSecuritySchemes =
|
|
504
|
+
const existingSecuritySchemes = validateSecuritySchemes(apiDefinition, correlationId).filter(Boolean);
|
|
505
505
|
|
|
506
506
|
extractProperties(apiDefinition, controllersDirectory, buildDirectory, correlationId);
|
|
507
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 = {};
|
package/lib/baseHandlers.js
CHANGED
|
@@ -34,9 +34,8 @@ 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).filter(key => key !== 'authorized');
|
|
38
38
|
|
|
39
|
-
delete schemes.authorized;
|
|
40
39
|
schemes.forEach((scheme) => {
|
|
41
40
|
const { error: schemeError } = con.security[scheme] || {};
|
|
42
41
|
if (schemeError) {
|
|
@@ -49,7 +48,7 @@ const unauthorizedHandler = (con, req, res) => {
|
|
|
49
48
|
const notImplemented = (con, req, res) => {
|
|
50
49
|
const { method } = req;
|
|
51
50
|
const path = req.url;
|
|
52
|
-
const error = new Error(`${
|
|
51
|
+
const error = new Error(`${method} ${path} defined in Swagger specification, but not implemented`);
|
|
53
52
|
|
|
54
53
|
error.statusCode = NOT_IMPLEMENTED_ERROR;
|
|
55
54
|
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[flow]) {
|
|
16
|
+
if (!security.flows || !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
|
@@ -19,8 +19,6 @@ 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';
|
|
24
22
|
import jwt from 'jsonwebtoken';
|
|
25
23
|
|
|
26
24
|
const UNAUTHORIZED_ERROR = 401;
|
|
@@ -93,7 +91,7 @@ const checkHeaders = (headers) => {
|
|
|
93
91
|
|
|
94
92
|
const checkScopes = (tokenScopes, defScopes, definition) => {
|
|
95
93
|
if (!tokenScopes) {
|
|
96
|
-
throw
|
|
94
|
+
throw getError('no scope in authorization token', UNAUTHORIZED_ERROR);
|
|
97
95
|
}
|
|
98
96
|
let claims = [];
|
|
99
97
|
let onBehalf = false;
|
|
@@ -126,10 +124,10 @@ const checkScopes = (tokenScopes, defScopes, definition) => {
|
|
|
126
124
|
}
|
|
127
125
|
const includedClaims = analyzedScope[CLAIMS_INDEX].split(CLAIMS_SEPARATOR);
|
|
128
126
|
const definitionClaims = Object.keys(includedDefinition);
|
|
129
|
-
const claimsIntersects =
|
|
127
|
+
const claimsIntersects = includedClaims.filter(cla => definitionClaims.includes(cla));
|
|
130
128
|
|
|
131
129
|
if (claimsIntersects.length !== includedClaims.length) {
|
|
132
|
-
throw getError(`incorrect claims included: ${
|
|
130
|
+
throw getError(`incorrect claims included: ${includedClaims.filter(cla => !claimsIntersects.includes(cla))}`, FORBIDDEN_ERROR);
|
|
133
131
|
}
|
|
134
132
|
claims = claims.concat(claimsIntersects);
|
|
135
133
|
}
|
package/package.json
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mimik/api-helper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
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": "mocha --reporter mochawesome test/ --recursive",
|
|
11
|
+
"test-ci": "c8 --reporter=lcov --reporter=text npm test",
|
|
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
|
-
},
|
|
21
15
|
"keywords": [
|
|
22
16
|
"mimik",
|
|
23
17
|
"microservice",
|
|
@@ -25,33 +19,38 @@
|
|
|
25
19
|
],
|
|
26
20
|
"author": "mimik technology inc <support@mimik.com> (https://developer.mimik.com/)",
|
|
27
21
|
"license": "MIT",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=24"
|
|
24
|
+
},
|
|
28
25
|
"repository": {
|
|
29
26
|
"type": "git",
|
|
30
27
|
"url": "https://bitbucket.org/mimiktech/api-helper"
|
|
31
28
|
},
|
|
32
29
|
"dependencies": {
|
|
33
|
-
"@mimik/request-
|
|
34
|
-
"@mimik/
|
|
35
|
-
"@mimik/
|
|
36
|
-
"@mimik/
|
|
37
|
-
"@mimik/swagger-helper": "^5.0.2",
|
|
30
|
+
"@mimik/request-retry": "^4.0.9",
|
|
31
|
+
"@mimik/response-helper": "^4.0.10",
|
|
32
|
+
"@mimik/sumologic-winston-logger": "^2.1.14",
|
|
33
|
+
"@mimik/swagger-helper": "^5.0.3",
|
|
38
34
|
"ajv-formats": "3.0.1",
|
|
39
|
-
"js-base64": "3.7.
|
|
40
|
-
"js-yaml":"4.1.
|
|
41
|
-
"jsonwebtoken": "9.0.
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"lodash.intersection": "4.4.0",
|
|
45
|
-
"openapi-backend": "5.13.0",
|
|
46
|
-
"swagger-client": "3.35.6"
|
|
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.37.0"
|
|
47
40
|
},
|
|
48
41
|
"devDependencies": {
|
|
49
42
|
"@eslint/js": "9.32.0",
|
|
50
43
|
"@mimik/eslint-plugin-document-env": "^2.0.8",
|
|
51
|
-
"@stylistic/eslint-plugin": "5.
|
|
44
|
+
"@stylistic/eslint-plugin": "5.9.0",
|
|
45
|
+
"c8": "11.0.0",
|
|
46
|
+
"chai": "6.2.2",
|
|
52
47
|
"eslint": "9.32.0",
|
|
53
48
|
"eslint-plugin-import": "2.32.0",
|
|
49
|
+
"esmock": "2.7.3",
|
|
50
|
+
"globals": "17.4.0",
|
|
54
51
|
"husky": "9.1.7",
|
|
55
|
-
"jsdoc-to-markdown": "9.1.
|
|
52
|
+
"jsdoc-to-markdown": "9.1.3",
|
|
53
|
+
"mocha": "11.7.5",
|
|
54
|
+
"mochawesome": "7.1.4"
|
|
56
55
|
}
|
|
57
56
|
}
|
package/.nycrc
DELETED
package/eslint.config.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import importPlugin from 'eslint-plugin-import';
|
|
2
|
-
import js from '@eslint/js';
|
|
3
|
-
import processDoc from '@mimik/eslint-plugin-document-env';
|
|
4
|
-
import stylistic from '@stylistic/eslint-plugin';
|
|
5
|
-
|
|
6
|
-
const MAX_LENGTH_LINE = 180;
|
|
7
|
-
const MAX_FUNCTION_PARAMETERS = 6;
|
|
8
|
-
const MAX_LINES_IN_FILES = 600;
|
|
9
|
-
const MAX_LINES_IN_FUNCTION = 150;
|
|
10
|
-
const MAX_STATEMENTS_IN_FUNCTION = 45;
|
|
11
|
-
const MIN_KEYS_IN_OBJECT = 10;
|
|
12
|
-
const MAX_COMPLEXITY = 30;
|
|
13
|
-
|
|
14
|
-
export default [
|
|
15
|
-
{
|
|
16
|
-
ignores: ['mochawesome-report/**', 'node_modules/**', 'dist/**'],
|
|
17
|
-
},
|
|
18
|
-
importPlugin.flatConfigs.recommended,
|
|
19
|
-
stylistic.configs.recommended,
|
|
20
|
-
js.configs.all,
|
|
21
|
-
{
|
|
22
|
-
plugins: {
|
|
23
|
-
processDoc,
|
|
24
|
-
},
|
|
25
|
-
languageOptions: {
|
|
26
|
-
ecmaVersion: 2022,
|
|
27
|
-
globals: {
|
|
28
|
-
console: 'readonly',
|
|
29
|
-
describe: 'readonly',
|
|
30
|
-
it: 'readonly',
|
|
31
|
-
require: 'readonly',
|
|
32
|
-
},
|
|
33
|
-
sourceType: 'module',
|
|
34
|
-
},
|
|
35
|
-
rules: {
|
|
36
|
-
'@stylistic/brace-style': ['warn', 'stroustrup', { allowSingleLine: true }],
|
|
37
|
-
'@stylistic/line-comment-position': ['off'],
|
|
38
|
-
'@stylistic/semi': ['error', 'always'],
|
|
39
|
-
'capitalized-comments': ['off'],
|
|
40
|
-
'complexity': ['error', MAX_COMPLEXITY],
|
|
41
|
-
'curly': ['off'],
|
|
42
|
-
'id-length': ['error', { exceptions: ['x', 'y', 'z', 'i', 'j', 'k'] }],
|
|
43
|
-
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
|
44
|
-
'import/no-unresolved': ['error', { amd: true, caseSensitiveStrict: true, commonjs: true }],
|
|
45
|
-
'init-declarations': ['off'],
|
|
46
|
-
'linebreak-style': ['off'],
|
|
47
|
-
'max-len': ['warn', MAX_LENGTH_LINE, { ignoreComments: true }],
|
|
48
|
-
'max-lines': ['warn', { max: MAX_LINES_IN_FILES, skipComments: true }],
|
|
49
|
-
'max-lines-per-function': ['warn', { max: MAX_LINES_IN_FUNCTION, skipComments: true }],
|
|
50
|
-
'max-params': ['error', MAX_FUNCTION_PARAMETERS],
|
|
51
|
-
'max-statements': ['warn', MAX_STATEMENTS_IN_FUNCTION],
|
|
52
|
-
'no-confusing-arrow': ['off'], // arrow isnt confusing
|
|
53
|
-
'no-inline-comments': ['off'],
|
|
54
|
-
'no-process-env': ['error'],
|
|
55
|
-
'no-ternary': ['off'],
|
|
56
|
-
'no-undefined': ['off'],
|
|
57
|
-
'one-var': ['error', 'never'],
|
|
58
|
-
'processDoc/validate-document-env': ['error'],
|
|
59
|
-
'quotes': ['warn', 'single'],
|
|
60
|
-
'sort-keys': ['error', 'asc', { caseSensitive: true, minKeys: MIN_KEYS_IN_OBJECT, natural: false }],
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
];
|