@mimik/api-helper 1.1.4 → 2.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 +62 -38
- package/eslint.config.js +63 -0
- package/index.js +115 -68
- package/lib/ajvHelpers.js +11 -10
- package/lib/baseHandlers.js +34 -26
- package/lib/common.js +1 -1
- package/lib/extract-helper.js +7 -9
- package/lib/oauthValidation-helper.js +7 -4
- package/lib/securityHandlers.js +100 -78
- package/package.json +19 -22
- package/.eslintrc +0 -43
package/README.md
CHANGED
|
@@ -1,29 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
</
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<a name="
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
<a name="module_api-helper"></a>
|
|
2
|
+
|
|
3
|
+
## api-helper
|
|
4
|
+
**Example**
|
|
5
|
+
```js
|
|
6
|
+
import apiHelper from '@mimik/api-helper';
|
|
7
|
+
or
|
|
8
|
+
import { apiSetup, securityLib, getAPIFile, validateSecuritySchemes, extractProperties, setupServerFiles } from '@mimik/api-helper';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
* [api-helper](#module_api-helper)
|
|
12
|
+
* _async_
|
|
13
|
+
* [~securityLib(config)](#module_api-helper..securityLib)
|
|
14
|
+
* [~apiSetup(setup, registeredOperations, securityHandlers, extraFormats, config, correlationId)](#module_api-helper..apiSetup) ⇒ <code>Promise</code>
|
|
15
|
+
* [~getAPIFile(apiFilename, correlationId, options)](#module_api-helper..getAPIFile) ⇒ <code>Promise</code>
|
|
16
|
+
* [~setupServerFiles(apiFilename, controllersDirectory, buidDirectory, correlationId, options)](#module_api-helper..setupServerFiles) ⇒ <code>Promise</code>
|
|
17
|
+
* _sync_
|
|
18
|
+
* [~validateSecuritySchemes(apiDefinition, correlationId)](#module_api-helper..validateSecuritySchemes) ⇒
|
|
19
|
+
* [~extractProperties(apiDefinition, controllersDirectory, buidDirectory, correlationId)](#module_api-helper..extractProperties) ⇒
|
|
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. |
|
|
44
|
+
|
|
45
|
+
<a name="module_api-helper..apiSetup"></a>
|
|
46
|
+
|
|
47
|
+
### api-helper~apiSetup(setup, registeredOperations, securityHandlers, extraFormats, config, correlationId) ⇒ <code>Promise</code>
|
|
24
48
|
Setup the API to be use for a service
|
|
25
49
|
|
|
26
|
-
**Kind**:
|
|
50
|
+
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
27
51
|
**Returns**: <code>Promise</code> - .
|
|
28
52
|
&fulfil {object} The API file itself.
|
|
29
53
|
**Category**: async
|
|
@@ -53,12 +77,12 @@ The default formats for validation are: `date`, `time`, `date-time`, `byte`, `uu
|
|
|
53
77
|
| config | <code>object</code> | Configuration of the service. |
|
|
54
78
|
| correlationId | <code>UUID.<string></code> | CorrelationId when logging activites. |
|
|
55
79
|
|
|
56
|
-
<a name="getAPIFile"></a>
|
|
80
|
+
<a name="module_api-helper..getAPIFile"></a>
|
|
57
81
|
|
|
58
|
-
|
|
82
|
+
### api-helper~getAPIFile(apiFilename, correlationId, options) ⇒ <code>Promise</code>
|
|
59
83
|
Gets the API file from swaggerhub and store it in the give PATH location.
|
|
60
84
|
|
|
61
|
-
**Kind**:
|
|
85
|
+
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
62
86
|
**Returns**: <code>Promise</code> - .
|
|
63
87
|
&fulfil {object} The API file itself.
|
|
64
88
|
**Category**: async
|
|
@@ -85,12 +109,12 @@ Gets the API file from swaggerhub and store it in the give PATH location.
|
|
|
85
109
|
| correlationId | <code>UUID.<string></code> | CorrelationId when logging activites. |
|
|
86
110
|
| 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. |
|
|
87
111
|
|
|
88
|
-
<a name="setupServerFiles"></a>
|
|
112
|
+
<a name="module_api-helper..setupServerFiles"></a>
|
|
89
113
|
|
|
90
|
-
|
|
114
|
+
### api-helper~setupServerFiles(apiFilename, controllersDirectory, buidDirectory, correlationId, options) ⇒ <code>Promise</code>
|
|
91
115
|
Setup and validates files for the server
|
|
92
116
|
|
|
93
|
-
**Kind**:
|
|
117
|
+
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
94
118
|
**Returns**: <code>Promise</code> - .
|
|
95
119
|
&fulfil {object} The API file, the API filename, the existing known security schemes and the defined security schemes.
|
|
96
120
|
**Category**: async
|
|
@@ -104,16 +128,16 @@ Setup and validates files for the server
|
|
|
104
128
|
| --- | --- | --- |
|
|
105
129
|
| apiFilename | <code>PATH.<string></code> | Name of the file where the API file will be stored. |
|
|
106
130
|
| controllersDirectory | <code>PATH.<string></code> | Directory to find the controller files. |
|
|
107
|
-
| buidDirectory | <code>PATH.<string></code> |
|
|
131
|
+
| buidDirectory | <code>PATH.<string></code> | Directory where the register file will be stored. |
|
|
108
132
|
| correlationId | <code>UUID.<string></code> | CorrelationId when logging activites. |
|
|
109
133
|
| options | <code>object</code> | Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey`` to access private API. |
|
|
110
134
|
|
|
111
|
-
<a name="validateSecuritySchemes"></a>
|
|
135
|
+
<a name="module_api-helper..validateSecuritySchemes"></a>
|
|
112
136
|
|
|
113
|
-
|
|
137
|
+
### api-helper~validateSecuritySchemes(apiDefinition, correlationId) ⇒
|
|
114
138
|
Validates the known SecuritySchemes: `SystemSecurity`, `AdminSecurity`, `UserSecurity`, `PeerSecurity`, `ApiKeySecurity`.
|
|
115
139
|
|
|
116
|
-
**Kind**:
|
|
140
|
+
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
117
141
|
**Returns**: An array of the known securitySchemes that are in the API definition.
|
|
118
142
|
**Category**: sync
|
|
119
143
|
**Throws**:
|
|
@@ -127,12 +151,12 @@ Validates the known SecuritySchemes: `SystemSecurity`, `AdminSecurity`, `UserSec
|
|
|
127
151
|
| apiDefinition | <code>object</code> | JSON object containing the API definition. |
|
|
128
152
|
| correlationId | <code>UUID.<string></code> | CorrelationId when logging activites. |
|
|
129
153
|
|
|
130
|
-
<a name="extractProperties"></a>
|
|
154
|
+
<a name="module_api-helper..extractProperties"></a>
|
|
131
155
|
|
|
132
|
-
|
|
156
|
+
### api-helper~extractProperties(apiDefinition, controllersDirectory, buidDirectory, correlationId) ⇒
|
|
133
157
|
Extracts the properties from API definiton and creates a file binding the handler with the controller operations.
|
|
134
158
|
|
|
135
|
-
**Kind**:
|
|
159
|
+
**Kind**: inner method of [<code>api-helper</code>](#module_api-helper)
|
|
136
160
|
**Returns**: null
|
|
137
161
|
**Category**: sync
|
|
138
162
|
**Throws**:
|
|
@@ -145,6 +169,6 @@ Extracts the properties from API definiton and creates a file binding the handle
|
|
|
145
169
|
| --- | --- | --- |
|
|
146
170
|
| apiDefinition | <code>object</code> | JSON object containing the API definition. |
|
|
147
171
|
| controllersDirectory | <code>PATH.<string></code> | Directory to find the controller files. |
|
|
148
|
-
| buidDirectory | <code>PATH.<string></code> |
|
|
172
|
+
| buidDirectory | <code>PATH.<string></code> | Directory where the register file will be stored. |
|
|
149
173
|
| correlationId | <code>UUID.<string></code> | CorrelationId when logging activites. |
|
|
150
174
|
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
];
|
package/index.js
CHANGED
|
@@ -1,47 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const pathLib = require('path');
|
|
4
|
-
const yaml = require('js-yaml');
|
|
5
|
-
const compact = require('lodash.compact');
|
|
6
|
-
const difference = require('lodash.difference');
|
|
7
|
-
const SwaggerClient = require('swagger-client');
|
|
8
|
-
const { Base64 } = require('js-base64');
|
|
9
|
-
|
|
10
|
-
const logger = require('@mimik/sumologic-winston-logger');
|
|
11
|
-
const { getRichError } = require('@mimik/response-helper');
|
|
12
|
-
const { rpRetry } = require('@mimik/request-retry');
|
|
13
|
-
|
|
14
|
-
const { saveProperties } = require('./lib/extract-helper');
|
|
15
|
-
const { validateOauth2, validateApiKey } = require('./lib/oauthValidation-helper');
|
|
16
|
-
const { ajvFormats } = require('./lib/ajvHelpers');
|
|
17
|
-
const securityLib = require('./lib/securityHandlers');
|
|
18
|
-
const baseHandlers = require('./lib/baseHandlers');
|
|
19
|
-
const {
|
|
20
|
-
LOCAL,
|
|
21
|
-
SET_ON,
|
|
22
|
-
SECURITY_ON,
|
|
23
|
-
SECURITY_OFF,
|
|
24
|
-
X_ROUTER_CONTROLLER,
|
|
25
|
-
EXTENSION,
|
|
1
|
+
import {
|
|
26
2
|
ADMIN_SECURITY,
|
|
27
|
-
SYSTEM_SECURITY,
|
|
28
|
-
PEER_SECURITY,
|
|
29
|
-
USER_SECURITY,
|
|
30
3
|
API_KEY_SECURITY,
|
|
4
|
+
API_PROVIDER_BITBUCKET,
|
|
5
|
+
API_PROVIDER_SWAGGERHUB,
|
|
6
|
+
API_SOURCE,
|
|
7
|
+
BITBUCKET,
|
|
31
8
|
CLIENT_CREDENTIALS,
|
|
9
|
+
DEFAULT_BITBUCKET_PASSWORD,
|
|
10
|
+
DEFAULT_BITBUCKET_USERNAME,
|
|
11
|
+
EXTENSION,
|
|
12
|
+
EXTENSION_YML,
|
|
32
13
|
IMPLICIT,
|
|
14
|
+
LOCAL,
|
|
15
|
+
PEER_SECURITY,
|
|
33
16
|
POSTFIX,
|
|
34
|
-
API_PROVIDER_SWAGGERHUB,
|
|
35
|
-
API_PROVIDER_BITBUCKET,
|
|
36
17
|
RESOLVED,
|
|
37
|
-
|
|
18
|
+
SECURITY_OFF,
|
|
19
|
+
SECURITY_ON,
|
|
20
|
+
SET_ON,
|
|
38
21
|
SWAGGER,
|
|
39
|
-
BITBUCKET,
|
|
40
22
|
SWAGGERHUB,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
23
|
+
SYSTEM_SECURITY,
|
|
24
|
+
USER_SECURITY,
|
|
25
|
+
X_ROUTER_CONTROLLER,
|
|
26
|
+
} from './lib/common.js';
|
|
27
|
+
import { validateApiKey, validateOauth2 } from './lib/oauthValidation-helper.js';
|
|
28
|
+
import { Base64 } from 'js-base64';
|
|
29
|
+
import OpenAPIBackend from 'openapi-backend';
|
|
30
|
+
import SwaggerClient from 'swagger-client';
|
|
31
|
+
import { ajvFormats } from './lib/ajvHelpers.js';
|
|
32
|
+
import baseHandlers from './lib/baseHandlers.js';
|
|
33
|
+
import compact from 'lodash.compact';
|
|
34
|
+
import difference from 'lodash.difference';
|
|
35
|
+
import fs from 'fs';
|
|
36
|
+
import { getRichError } from '@mimik/response-helper';
|
|
37
|
+
import { load } from 'js-yaml';
|
|
38
|
+
import logger from '@mimik/sumologic-winston-logger';
|
|
39
|
+
import pathLib from 'path';
|
|
40
|
+
import { rpRetry } from '@mimik/request-retry';
|
|
41
|
+
import { saveProperties } from './lib/extract-helper.js';
|
|
42
|
+
import { securityLib } from './lib/securityHandlers.js';
|
|
43
|
+
/**
|
|
44
|
+
* @module api-helper
|
|
45
|
+
* @example
|
|
46
|
+
* import apiHelper from '@mimik/api-helper';
|
|
47
|
+
* or
|
|
48
|
+
* import { apiSetup, securityLib, getAPIFile, validateSecuritySchemes, extractProperties, setupServerFiles } from '@mimik/api-helper';
|
|
49
|
+
*/
|
|
50
|
+
const EMPTY = 0;
|
|
51
|
+
const TAB = 2;
|
|
52
|
+
const FULL_NAME_LENGTH = 4;
|
|
53
|
+
const CUSTOMER_INDEX = 0;
|
|
54
|
+
const API_NAME_INDEX = 1;
|
|
55
|
+
const API_VERSION_INDEX = 2;
|
|
56
|
+
const POSTFIX_INDEX = 3;
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* Implement the security flows for the API.
|
|
60
|
+
*
|
|
61
|
+
* @function securityLib
|
|
62
|
+
* @category async
|
|
63
|
+
* @requires @mimik/swagger-helper
|
|
64
|
+
* @requires jsonwebtoken
|
|
65
|
+
* @requires lodash
|
|
66
|
+
* @param {object} config - Configuration of the service.
|
|
67
|
+
* &fulfil {object} The API file itself.
|
|
68
|
+
* @throws {Promise} An error is thrown if the initiatilization failed.
|
|
69
|
+
*
|
|
70
|
+
* This function is used to setup the following security handlers for the API:
|
|
71
|
+
* - `SystemSecurity` - used for the system operations, like /system, /onbehalf
|
|
72
|
+
* - `AdminSecurity` - used for the admin operations, like /admin,
|
|
73
|
+
* - `UserSecurity` - used for the user operations, like /user,
|
|
74
|
+
* - `ApiKeySecurity` - used for the API key operations, like /apikey,
|
|
75
|
+
* The security handlers are used to validate the tokens and scopes for the API operations.
|
|
76
|
+
*/
|
|
77
|
+
export { securityLib };
|
|
45
78
|
|
|
46
79
|
/**
|
|
47
80
|
*
|
|
@@ -77,7 +110,7 @@ const {
|
|
|
77
110
|
*
|
|
78
111
|
* The default formats for validation are: `date`, `time`, `date-time`, `byte`, `uuid`, `uri`, `email`, `ipv4`, `ipv6`, `semver`, `ip`.
|
|
79
112
|
*/
|
|
80
|
-
const apiSetup = (setup, registeredOperations, securityHandlers, extraFormats, config, correlationId) => {
|
|
113
|
+
export const apiSetup = (setup, registeredOperations, securityHandlers, extraFormats, config, correlationId) => {
|
|
81
114
|
const { apiFilename, existingSecuritySchemes, definedSecuritySchemes } = setup;
|
|
82
115
|
const {
|
|
83
116
|
SystemSecurity,
|
|
@@ -125,7 +158,7 @@ const apiSetup = (setup, registeredOperations, securityHandlers, extraFormats, c
|
|
|
125
158
|
const securityHandlerNames = Object.keys(securityHandlers);
|
|
126
159
|
const unusedSecuritySchemes = difference(securityHandlerNames, definedSecuritySchemes);
|
|
127
160
|
|
|
128
|
-
if (unusedSecuritySchemes.length !==
|
|
161
|
+
if (unusedSecuritySchemes.length !== EMPTY) throw getRichError('System', 'unused handlers for security schemes', { unusedSecuritySchemes });
|
|
129
162
|
|
|
130
163
|
remainingSecurities.forEach((securityScheme) => {
|
|
131
164
|
if (!securityHandlerNames.includes(securityScheme) && !securityHandlers[securityScheme].notEnabled) {
|
|
@@ -138,7 +171,7 @@ const apiSetup = (setup, registeredOperations, securityHandlers, extraFormats, c
|
|
|
138
171
|
}
|
|
139
172
|
});
|
|
140
173
|
}
|
|
141
|
-
else if (remainingSecurities.length !==
|
|
174
|
+
else if (remainingSecurities.length !== EMPTY) throw getRichError('System', 'missing handlers for security schemes', { missingSecuritySchemes: remainingSecurities });
|
|
142
175
|
api.init()
|
|
143
176
|
.catch((err) => {
|
|
144
177
|
throw getRichError('System', 'could not initialize the api', { api }, err);
|
|
@@ -176,8 +209,8 @@ const apiSetup = (setup, registeredOperations, securityHandlers, extraFormats, c
|
|
|
176
209
|
* "apiApiKey": "apiKey for access private API on swaggerhub, can be optional if the API is accessible publically"
|
|
177
210
|
* }
|
|
178
211
|
*/
|
|
179
|
-
const getAPIFile = (apiFilename, correlationId, options) => {
|
|
180
|
-
const swaggerOptions =
|
|
212
|
+
export const getAPIFile = (apiFilename, correlationId, options) => {
|
|
213
|
+
const swaggerOptions = spec => ({
|
|
181
214
|
spec,
|
|
182
215
|
allowMetaPatches: false,
|
|
183
216
|
skipNormalization: true,
|
|
@@ -197,7 +230,9 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
197
230
|
return Promise.reject(getRichError('System', 'file system error', { apiFilename }, err));
|
|
198
231
|
}
|
|
199
232
|
if (apiDefinition) {
|
|
200
|
-
try {
|
|
233
|
+
try {
|
|
234
|
+
apiDefinition = JSON.parse(apiDefinition);
|
|
235
|
+
}
|
|
201
236
|
catch (err) {
|
|
202
237
|
return Promise.reject(getRichError('System', 'wrong file format', { apiFilename }, err));
|
|
203
238
|
}
|
|
@@ -206,11 +241,13 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
206
241
|
throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
|
|
207
242
|
})
|
|
208
243
|
.then((apiDefinitionResult) => {
|
|
209
|
-
if (apiDefinitionResult.errors.length !==
|
|
244
|
+
if (apiDefinitionResult.errors.length !== EMPTY) {
|
|
210
245
|
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
211
246
|
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
212
247
|
}
|
|
213
|
-
try {
|
|
248
|
+
try {
|
|
249
|
+
fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
|
|
250
|
+
}
|
|
214
251
|
catch (err) {
|
|
215
252
|
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
216
253
|
}
|
|
@@ -228,13 +265,17 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
228
265
|
let fileName;
|
|
229
266
|
let apiDirectory;
|
|
230
267
|
|
|
231
|
-
try {
|
|
268
|
+
try {
|
|
269
|
+
fileName = pathLib.basename(apiFilename);
|
|
270
|
+
}
|
|
232
271
|
catch (err) { return Promise.reject(getRichError('System', 'file name error', { apiFilename }, err)); }
|
|
233
|
-
try {
|
|
272
|
+
try {
|
|
273
|
+
apiDirectory = pathLib.dirname(apiFilename);
|
|
274
|
+
}
|
|
234
275
|
catch (err) { return Promise.reject(getRichError('System', 'directory name error', { apiFilename }, err)); }
|
|
235
276
|
const params = fileName.split('_');
|
|
236
277
|
|
|
237
|
-
if (params.length !==
|
|
278
|
+
if (params.length !== FULL_NAME_LENGTH || params[POSTFIX_INDEX] !== POSTFIX) {
|
|
238
279
|
return Promise.reject(getRichError('System', 'wrong api name', { apiFilename }));
|
|
239
280
|
}
|
|
240
281
|
try {
|
|
@@ -257,7 +298,7 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
257
298
|
try {
|
|
258
299
|
switch (provider) {
|
|
259
300
|
case SWAGGERHUB: {
|
|
260
|
-
opts.url = `${API_PROVIDER_SWAGGERHUB}/${params[
|
|
301
|
+
opts.url = `${API_PROVIDER_SWAGGERHUB}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}/${params[API_VERSION_INDEX]}?${RESOLVED}`;
|
|
261
302
|
if (apiInfo.apiApiKey) opts.headers.Authorization = apiInfo.apiApiKey;
|
|
262
303
|
break;
|
|
263
304
|
}
|
|
@@ -268,11 +309,13 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
268
309
|
if (apiInfo.apiBasicAuth.username === DEFAULT_BITBUCKET_USERNAME || apiInfo.apiBasicAuth.password === DEFAULT_BITBUCKET_PASSWORD) {
|
|
269
310
|
throw getRichError('Parameter', 'missing username/password for accessing Bitbucket', { apiFilename });
|
|
270
311
|
}
|
|
271
|
-
try {
|
|
312
|
+
try {
|
|
313
|
+
opts.headers.Authorization = `Basic ${Base64.encode(`${apiInfo.apiBasicAuth.username}:${apiInfo.apiBasicAuth.password}`)}`;
|
|
314
|
+
}
|
|
272
315
|
catch (err) {
|
|
273
316
|
throw getRichError('System', 'could not create basicAuth', { apiFilename }, err);
|
|
274
317
|
}
|
|
275
|
-
opts.url = `${API_PROVIDER_BITBUCKET}/${params[
|
|
318
|
+
opts.url = `${API_PROVIDER_BITBUCKET}/${params[CUSTOMER_INDEX]}/${params[API_NAME_INDEX]}${API_SOURCE}/${params[API_VERSION_INDEX]}/${SWAGGER}${EXTENSION_YML}`;
|
|
276
319
|
break;
|
|
277
320
|
}
|
|
278
321
|
default: {
|
|
@@ -299,7 +342,7 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
299
342
|
.then((result) => {
|
|
300
343
|
let resultJSON = result;
|
|
301
344
|
|
|
302
|
-
if (typeof result !== 'object') resultJSON =
|
|
345
|
+
if (typeof result !== 'object') resultJSON = load(result);
|
|
303
346
|
return SwaggerClient.resolve(swaggerOptions(resultJSON));
|
|
304
347
|
})
|
|
305
348
|
.catch((err) => {
|
|
@@ -309,11 +352,13 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
309
352
|
throw getRichError('System', 'could not resolve apiDefiniton', { apiFilename }, err);
|
|
310
353
|
})
|
|
311
354
|
.then((apiDefinitionResult) => {
|
|
312
|
-
if (apiDefinitionResult.errors.length !==
|
|
355
|
+
if (apiDefinitionResult.errors.length !== EMPTY) {
|
|
313
356
|
logger.error('errors while resolving definition', { errors: apiDefinitionResult.errors }, correlationId);
|
|
314
357
|
throw getRichError('Parameter', 'errors while resolving definition', { apiFilename, errors: apiDefinitionResult.errors });
|
|
315
358
|
}
|
|
316
|
-
try {
|
|
359
|
+
try {
|
|
360
|
+
fs.writeFileSync(apiFilename, JSON.stringify(apiDefinitionResult.spec, null, TAB));
|
|
361
|
+
}
|
|
317
362
|
catch (err) {
|
|
318
363
|
throw getRichError('System', 'file system error', { apiFilename }, err);
|
|
319
364
|
}
|
|
@@ -334,7 +379,7 @@ const getAPIFile = (apiFilename, correlationId, options) => {
|
|
|
334
379
|
* @return An array of the known securitySchemes that are in the API definition.
|
|
335
380
|
* @throws An error is thrown for the first validation fails.
|
|
336
381
|
*/
|
|
337
|
-
const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
382
|
+
export const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
338
383
|
const existingSecuritySchemes = [];
|
|
339
384
|
|
|
340
385
|
if (apiDefinition.components?.securitySchemes) {
|
|
@@ -361,17 +406,19 @@ const validateSecuritySchemes = (apiDefinition, correlationId) => {
|
|
|
361
406
|
* @requires fs
|
|
362
407
|
* @param {object} apiDefinition - JSON object containing the API definition.
|
|
363
408
|
* @param {PATH.<string>} controllersDirectory - Directory to find the controller files.
|
|
364
|
-
* @param {PATH.<string>} buidDirectory
|
|
409
|
+
* @param {PATH.<string>} buidDirectory - Directory where the register file will be stored.
|
|
365
410
|
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
366
411
|
* @return null
|
|
367
412
|
* @throws An error is thrown for many reasons, like operationId does not exist in controllers, controller dies not exist...
|
|
368
413
|
*/
|
|
369
|
-
const extractProperties = (apiDefinition, controllersDirectory, buildDirectory, correlationId) => {
|
|
414
|
+
export const extractProperties = (apiDefinition, controllersDirectory, buildDirectory, correlationId) => {
|
|
370
415
|
const result = {};
|
|
371
416
|
const { paths } = apiDefinition;
|
|
372
417
|
let controllersDirectoryName;
|
|
373
418
|
|
|
374
|
-
try {
|
|
419
|
+
try {
|
|
420
|
+
controllersDirectoryName = pathLib.basename(controllersDirectory);
|
|
421
|
+
}
|
|
375
422
|
catch (err) {
|
|
376
423
|
throw getRichError('System', 'directory name error', { controllersDirectory }, err);
|
|
377
424
|
}
|
|
@@ -395,10 +442,7 @@ const extractProperties = (apiDefinition, controllersDirectory, buildDirectory,
|
|
|
395
442
|
const operation = path[verb];
|
|
396
443
|
|
|
397
444
|
if (operation.operationId) {
|
|
398
|
-
if (
|
|
399
|
-
throw getRichError('System', 'missing property', { property: X_ROUTER_CONTROLLER });
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
445
|
+
if (operation[X_ROUTER_CONTROLLER]) {
|
|
402
446
|
const controller = operation[X_ROUTER_CONTROLLER];
|
|
403
447
|
const controllerFilename = `${controllersDirectory}/${controller}${EXTENSION}`;
|
|
404
448
|
|
|
@@ -413,16 +457,19 @@ const extractProperties = (apiDefinition, controllersDirectory, buildDirectory,
|
|
|
413
457
|
try {
|
|
414
458
|
const file = fs.readFileSync(controllerFilename, 'utf8');
|
|
415
459
|
if (!file.includes(`${operation.operationId},`)
|
|
416
|
-
|
|
417
|
-
|
|
460
|
+
&& !file.includes(`${operation.operationId}:`)
|
|
461
|
+
&& !file.includes(`export ${operation.operationId}`)) { // code must be linted before
|
|
418
462
|
throw getRichError('System', 'missing operationId in controller file', { controllerFilename, operationId: operation.operationId });
|
|
419
463
|
}
|
|
420
464
|
}
|
|
421
465
|
catch (err) {
|
|
422
466
|
throw getRichError('System', 'file system error', { controllerFilename, operationId: operation.operationId }, err);
|
|
423
467
|
}
|
|
424
|
-
if (
|
|
425
|
-
else result[controller]
|
|
468
|
+
if (result[controller]) result[controller].push(operation.operationId);
|
|
469
|
+
else result[controller] = [operation.operationId];
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
throw getRichError('System', 'missing property', { property: X_ROUTER_CONTROLLER });
|
|
426
473
|
}
|
|
427
474
|
}
|
|
428
475
|
});
|
|
@@ -445,14 +492,14 @@ const extractProperties = (apiDefinition, controllersDirectory, buildDirectory,
|
|
|
445
492
|
* @requires path
|
|
446
493
|
* @param {PATH.<string>} apiFilename - Name of the file where the API file will be stored.
|
|
447
494
|
* @param {PATH.<string>} controllersDirectory - Directory to find the controller files.
|
|
448
|
-
* @param {PATH.<string>} buidDirectory
|
|
495
|
+
* @param {PATH.<string>} buidDirectory - Directory where the register file will be stored.
|
|
449
496
|
* @param {UUID.<string>} correlationId - CorrelationId when logging activites.
|
|
450
497
|
* @param {object} options - Options associated with the call. Use to pass `metrics` to `rpRetry` and `apiKey`` to access private API.
|
|
451
498
|
* @return {Promise}.
|
|
452
499
|
* &fulfil {object} The API file, the API filename, the existing known security schemes and the defined security schemes.
|
|
453
500
|
* @throws {Promise} An error is thrown for many reasons assocated with getAPIFile or validateSecuritySchemes or extractProperties.
|
|
454
501
|
*/
|
|
455
|
-
const setupServerFiles = (apiFilename, controllersDirectory, buildDirectory, correlationId, options) => getAPIFile(apiFilename, correlationId, options)
|
|
502
|
+
export const setupServerFiles = (apiFilename, controllersDirectory, buildDirectory, correlationId, options) => getAPIFile(apiFilename, correlationId, options)
|
|
456
503
|
.then((apiDefinition) => {
|
|
457
504
|
const existingSecuritySchemes = compact(validateSecuritySchemes(apiDefinition, correlationId));
|
|
458
505
|
|
|
@@ -469,7 +516,7 @@ const setupServerFiles = (apiFilename, controllersDirectory, buildDirectory, cor
|
|
|
469
516
|
};
|
|
470
517
|
});
|
|
471
518
|
|
|
472
|
-
|
|
519
|
+
export default {
|
|
473
520
|
apiSetup,
|
|
474
521
|
securityLib,
|
|
475
522
|
getAPIFile,
|
package/lib/ajvHelpers.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import { DEFAULT_FORMATS } from './common.js';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const ajvFormats = (origFormats) => (ajv) => {
|
|
4
|
+
const ajvFormats = origFormats => (ajv) => {
|
|
6
5
|
const extraFormats = {
|
|
7
6
|
semver: {
|
|
8
7
|
type: 'string',
|
|
9
|
-
|
|
8
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
9
|
+
validate: /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/u,
|
|
10
10
|
},
|
|
11
11
|
ip: {
|
|
12
12
|
type: 'string',
|
|
13
|
-
|
|
13
|
+
// eslint-disable-next-line prefer-named-capture-group
|
|
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,
|
|
14
15
|
},
|
|
15
16
|
};
|
|
16
17
|
const libFormats = DEFAULT_FORMATS;
|
|
@@ -18,14 +19,14 @@ const ajvFormats = (origFormats) => (ajv) => {
|
|
|
18
19
|
|
|
19
20
|
if (!formats) formats = {};
|
|
20
21
|
Object.keys(formats).forEach((format) => {
|
|
21
|
-
if (
|
|
22
|
-
else
|
|
22
|
+
if (formats[format].type) extraFormats[format] = formats[format];
|
|
23
|
+
else libFormats.push(format);
|
|
23
24
|
});
|
|
24
25
|
addFormats(ajv, libFormats);
|
|
25
|
-
Object.keys(extraFormats).forEach(
|
|
26
|
+
Object.keys(extraFormats).forEach(format => ajv.addFormat(format, extraFormats[format]));
|
|
26
27
|
return ajv;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
export {
|
|
30
31
|
ajvFormats,
|
|
31
32
|
};
|
package/lib/baseHandlers.js
CHANGED
|
@@ -1,84 +1,92 @@
|
|
|
1
|
-
|
|
1
|
+
import { rejectRequest } from '@mimik/swagger-helper';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const PARAMETER_ERROR = 400;
|
|
4
|
+
const NOT_FOUND_ERROR = 404;
|
|
5
|
+
const UNAUTHORIZED_ERROR = 401;
|
|
6
|
+
const NOT_IMPLEMENTED_ERROR = 501;
|
|
7
|
+
|
|
8
|
+
const validationFail = (con, req, res) => {
|
|
4
9
|
const error = new Error('Failed schema validation');
|
|
5
10
|
|
|
6
|
-
error.statusCode =
|
|
11
|
+
error.statusCode = PARAMETER_ERROR;
|
|
7
12
|
error.info = {
|
|
8
13
|
method: req.method,
|
|
9
14
|
path: req.url,
|
|
10
|
-
errors:
|
|
11
|
-
warnings:
|
|
15
|
+
errors: con.validation.errors,
|
|
16
|
+
warnings: con.validation.warnings || [],
|
|
12
17
|
};
|
|
13
|
-
rejectRequest(error,
|
|
18
|
+
rejectRequest(error, con, res);
|
|
14
19
|
};
|
|
15
20
|
|
|
16
|
-
const notFound = (
|
|
21
|
+
const notFound = (con, req, res) => {
|
|
17
22
|
const path = req.url;
|
|
18
23
|
const error = new Error(`path ${path} not defined in Swagger specification`);
|
|
19
24
|
|
|
20
|
-
error.statusCode =
|
|
25
|
+
error.statusCode = NOT_FOUND_ERROR;
|
|
21
26
|
error.info = {
|
|
22
27
|
method: req.method,
|
|
23
28
|
path,
|
|
24
29
|
};
|
|
25
|
-
rejectRequest(error,
|
|
30
|
+
rejectRequest(error, con, res);
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
const unauthorizedHandler = (
|
|
33
|
+
const unauthorizedHandler = (con, req, res) => {
|
|
29
34
|
let error = new Error('Unauthorized');
|
|
30
35
|
|
|
31
|
-
error.statusCode =
|
|
32
|
-
const schemes = Object.keys(
|
|
36
|
+
error.statusCode = UNAUTHORIZED_ERROR;
|
|
37
|
+
const schemes = Object.keys(con.security);
|
|
33
38
|
|
|
34
39
|
delete schemes.authorized;
|
|
35
40
|
schemes.forEach((scheme) => {
|
|
36
|
-
|
|
41
|
+
const { error: schemeError } = con.security[scheme] || {};
|
|
42
|
+
if (schemeError) {
|
|
43
|
+
error = schemeError;
|
|
44
|
+
}
|
|
37
45
|
});
|
|
38
|
-
rejectRequest(error,
|
|
46
|
+
rejectRequest(error, con, res);
|
|
39
47
|
};
|
|
40
48
|
|
|
41
|
-
const notImplemented = (
|
|
49
|
+
const notImplemented = (con, req, res) => {
|
|
42
50
|
const { method } = req;
|
|
43
51
|
const path = req.url;
|
|
44
52
|
const error = new Error(`${req.method} ${path} defined in Swagger specification, but no implemented`);
|
|
45
53
|
|
|
46
|
-
error.statusCode =
|
|
54
|
+
error.statusCode = NOT_IMPLEMENTED_ERROR;
|
|
47
55
|
error.info = {
|
|
48
56
|
method,
|
|
49
57
|
path,
|
|
50
|
-
operationId:
|
|
58
|
+
operationId: con.operation.operationId,
|
|
51
59
|
};
|
|
52
|
-
rejectRequest(error,
|
|
60
|
+
rejectRequest(error, con, res);
|
|
53
61
|
};
|
|
54
62
|
|
|
55
|
-
const methodNotAllowed = (
|
|
63
|
+
const methodNotAllowed = (con, req, res) => {
|
|
56
64
|
const { method } = req;
|
|
57
65
|
const path = req.url;
|
|
58
66
|
const error = new Error(`path ${path} defined in Swagger specification, but the method ${method} is not defined`);
|
|
59
67
|
|
|
60
|
-
error.statusCode =
|
|
68
|
+
error.statusCode = NOT_IMPLEMENTED_ERROR;
|
|
61
69
|
error.info = {
|
|
62
70
|
method,
|
|
63
71
|
path,
|
|
64
72
|
};
|
|
65
|
-
rejectRequest(error,
|
|
73
|
+
rejectRequest(error, con, res);
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
/*
|
|
69
|
-
const postResponseHandler = (
|
|
70
|
-
const valid =
|
|
71
|
-
console.log('----->',
|
|
77
|
+
const postResponseHandler = (con, req, res) => {
|
|
78
|
+
const valid = con.api.validateResponse(con.response, con.operation);
|
|
79
|
+
console.log('----->', con.response);
|
|
72
80
|
console.log('----->', valid);
|
|
73
81
|
if (valid.errors) {
|
|
74
82
|
// response validation failed
|
|
75
83
|
return res.status(502).json({ status: 502, err: valid.errors });
|
|
76
84
|
}
|
|
77
|
-
return res.status(200).json(
|
|
85
|
+
return res.status(200).json(con.response);
|
|
78
86
|
};
|
|
79
87
|
*/
|
|
80
88
|
|
|
81
|
-
|
|
89
|
+
export default {
|
|
82
90
|
validationFail,
|
|
83
91
|
notFound,
|
|
84
92
|
unauthorizedHandler,
|
package/lib/common.js
CHANGED
package/lib/extract-helper.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const { EXTENSION, REGISTER } = require('./common');
|
|
1
|
+
import { EXTENSION, REGISTER } from './common.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { getRichError } from '@mimik/response-helper';
|
|
4
|
+
import logger from '@mimik/sumologic-winston-logger';
|
|
7
5
|
|
|
8
6
|
const saveProperties = (extractResult, buildDirectory, controllersDirectoryName, correlationId) => {
|
|
9
7
|
try {
|
|
@@ -36,9 +34,9 @@ const saveProperties = (extractResult, buildDirectory, controllersDirectoryName,
|
|
|
36
34
|
itemToSave += ` ${operationId},\n`;
|
|
37
35
|
operationIds.push(operationId);
|
|
38
36
|
});
|
|
39
|
-
stringToSave += `
|
|
37
|
+
stringToSave += `import {\n${itemToSave}} from '../${controllersDirectoryName}/${controller}';\n`;
|
|
40
38
|
});
|
|
41
|
-
stringToSave += '\
|
|
39
|
+
stringToSave += '\nexport {\n';
|
|
42
40
|
itemToSave = '';
|
|
43
41
|
operationIds.forEach((operationId) => {
|
|
44
42
|
itemToSave += ` ${operationId},\n`;
|
|
@@ -53,6 +51,6 @@ const saveProperties = (extractResult, buildDirectory, controllersDirectoryName,
|
|
|
53
51
|
}
|
|
54
52
|
};
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
export {
|
|
57
55
|
saveProperties,
|
|
58
56
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
API_KEY_IN,
|
|
3
|
+
API_KEY_NAME,
|
|
4
|
+
OAUTH2,
|
|
5
|
+
} from './common.js';
|
|
6
|
+
import { getRichError } from '@mimik/response-helper';
|
|
4
7
|
|
|
5
8
|
const validateOauth2 = (securitySchemes, securityType, flow) => {
|
|
6
9
|
if (securitySchemes) {
|
|
@@ -36,7 +39,7 @@ const validateApiKey = (securitySchemes, securityType) => {
|
|
|
36
39
|
return null;
|
|
37
40
|
};
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
export {
|
|
40
43
|
validateOauth2,
|
|
41
44
|
validateApiKey,
|
|
42
45
|
};
|
package/lib/securityHandlers.js
CHANGED
|
@@ -1,33 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
const intersection = require('lodash.intersection');
|
|
3
|
-
const difference = require('lodash.difference');
|
|
4
|
-
|
|
5
|
-
const { TOKEN_PARAMS } = require('@mimik/swagger-helper');
|
|
6
|
-
const {
|
|
7
|
-
AUTHORIZATION,
|
|
1
|
+
import {
|
|
8
2
|
ADMIN,
|
|
9
|
-
|
|
3
|
+
ADMIN_SECURITY,
|
|
4
|
+
API_KEY_NAME,
|
|
5
|
+
AUTHORIZATION,
|
|
6
|
+
BEARERS,
|
|
7
|
+
CLAIMS_DEFINITION,
|
|
8
|
+
CLAIMS_SEPARATOR,
|
|
10
9
|
CLIENT,
|
|
10
|
+
CLUSTER,
|
|
11
11
|
NO_GENERIC,
|
|
12
12
|
ON_BEHALF,
|
|
13
|
-
|
|
13
|
+
RESOURCE_SEPARATOR,
|
|
14
14
|
SCOPES_SEPARATOR,
|
|
15
15
|
SCOPE_CLAIMS_SEPARATOR,
|
|
16
|
-
|
|
17
|
-
CLAIMS_SEPARATOR,
|
|
18
|
-
BEARERS,
|
|
19
|
-
USER,
|
|
20
|
-
CLUSTER,
|
|
21
|
-
API_KEY_NAME,
|
|
22
|
-
ADMIN_SECURITY,
|
|
16
|
+
SUB_ADMIN,
|
|
23
17
|
SYSTEM_SECURITY,
|
|
18
|
+
USER,
|
|
24
19
|
USER_SECURITY,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
} from './common';
|
|
21
|
+
import { TOKEN_PARAMS } from '@mimik/swagger-helper';
|
|
22
|
+
import difference from 'lodash.difference';
|
|
23
|
+
import intersection from 'lodash.intersection';
|
|
24
|
+
import jwt from 'jsonwebtoken';
|
|
25
|
+
|
|
26
|
+
const UNAUTHORIZED_ERROR = 401;
|
|
27
|
+
const FORBIDDEN_ERROR = 403;
|
|
28
|
+
const SYSTEM_ERROR = 500;
|
|
29
|
+
const EMPTY = 0;
|
|
30
|
+
const SINGLE = 1;
|
|
31
|
+
const FIRST_AUTHORIZATION = 0;
|
|
32
|
+
const BEARER_INDEX = 0;
|
|
33
|
+
const TOKEN_INDEX = 1;
|
|
34
|
+
const BEARER_LENGTH = 2;
|
|
35
|
+
const FIRST = 1;
|
|
36
|
+
const SECOND = 2;
|
|
37
|
+
const SCOPE_INDEX = 0;
|
|
38
|
+
const CLAIMS_INDEX = 1;
|
|
39
|
+
const RESOURCE_INDEX = 0;
|
|
40
|
+
|
|
41
|
+
const getScopes = (conf, securityType) => {
|
|
28
42
|
let scopes = [];
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
conf.operation.security.forEach((security) => {
|
|
31
45
|
if (security[securityType]) scopes = scopes.concat(security[securityType]);
|
|
32
46
|
});
|
|
33
47
|
return scopes;
|
|
@@ -43,36 +57,38 @@ const getError = (message, statusCode) => {
|
|
|
43
57
|
const checkToken = (authToken) => {
|
|
44
58
|
let token;
|
|
45
59
|
|
|
46
|
-
try {
|
|
60
|
+
try {
|
|
61
|
+
token = jwt.decode(authToken);
|
|
62
|
+
}
|
|
47
63
|
catch (err) {
|
|
48
|
-
err.statusCode =
|
|
64
|
+
err.statusCode = UNAUTHORIZED_ERROR;
|
|
49
65
|
throw err;
|
|
50
66
|
}
|
|
51
67
|
if (!token) {
|
|
52
|
-
throw getError('invalid token',
|
|
68
|
+
throw getError('invalid token', UNAUTHORIZED_ERROR);
|
|
53
69
|
}
|
|
54
70
|
return token;
|
|
55
71
|
};
|
|
56
72
|
|
|
57
73
|
const checkHeaders = (headers) => {
|
|
58
74
|
if (!headers) {
|
|
59
|
-
throw getError('missing header',
|
|
75
|
+
throw getError('missing header', UNAUTHORIZED_ERROR);
|
|
60
76
|
}
|
|
61
|
-
const authNames = Object.keys(headers).filter(
|
|
77
|
+
const authNames = Object.keys(headers).filter(key => key.toLowerCase() === AUTHORIZATION);
|
|
62
78
|
const authNamesLength = authNames.length;
|
|
63
79
|
|
|
64
|
-
if (authNamesLength ===
|
|
65
|
-
throw getError(`missing ${AUTHORIZATION} header`,
|
|
80
|
+
if (authNamesLength === EMPTY) {
|
|
81
|
+
throw getError(`missing ${AUTHORIZATION} header`, UNAUTHORIZED_ERROR);
|
|
66
82
|
}
|
|
67
|
-
if (authNamesLength !==
|
|
68
|
-
throw getError(`duplicated ${AUTHORIZATION} header`,
|
|
83
|
+
if (authNamesLength !== SINGLE) {
|
|
84
|
+
throw getError(`duplicated ${AUTHORIZATION} header`, UNAUTHORIZED_ERROR);
|
|
69
85
|
}
|
|
70
|
-
const auth = headers[authNames[
|
|
86
|
+
const auth = headers[authNames[FIRST_AUTHORIZATION]].split(' ');
|
|
71
87
|
|
|
72
|
-
if (!BEARERS.includes(auth[
|
|
73
|
-
throw getError(`authorization type incorrect: ${auth[
|
|
88
|
+
if (!BEARERS.includes(auth[BEARER_INDEX]) || auth.length !== BEARER_LENGTH) {
|
|
89
|
+
throw getError(`authorization type incorrect: ${auth[BEARER_INDEX]}`, UNAUTHORIZED_ERROR);
|
|
74
90
|
}
|
|
75
|
-
return auth[
|
|
91
|
+
return auth[TOKEN_INDEX];
|
|
76
92
|
};
|
|
77
93
|
|
|
78
94
|
const checkScopes = (tokenScopes, defScopes, definition) => {
|
|
@@ -82,52 +98,52 @@ const checkScopes = (tokenScopes, defScopes, definition) => {
|
|
|
82
98
|
let claims = [];
|
|
83
99
|
let onBehalf = false;
|
|
84
100
|
|
|
85
|
-
if (defScopes && defScopes.length !==
|
|
101
|
+
if (defScopes && defScopes.length !== EMPTY) {
|
|
86
102
|
const currentScopes = tokenScopes.split(SCOPES_SEPARATOR);
|
|
87
103
|
const intersects = [];
|
|
88
|
-
let resourceIndex =
|
|
104
|
+
let resourceIndex = FIRST;
|
|
89
105
|
|
|
90
106
|
currentScopes.forEach((currentScope) => {
|
|
91
107
|
const analyzedScope = currentScope.split(SCOPE_CLAIMS_SEPARATOR);
|
|
92
|
-
const analyzedResource = analyzedScope[
|
|
108
|
+
const analyzedResource = analyzedScope[SCOPE_INDEX].split(RESOURCE_SEPARATOR);
|
|
93
109
|
|
|
94
|
-
if (analyzedResource[
|
|
110
|
+
if (analyzedResource[RESOURCE_INDEX] === ON_BEHALF) {
|
|
95
111
|
onBehalf = true;
|
|
96
|
-
resourceIndex =
|
|
112
|
+
resourceIndex = SECOND; // legacy handling
|
|
97
113
|
}
|
|
98
114
|
|
|
99
|
-
if (defScopes.includes(analyzedScope[
|
|
100
|
-
if (analyzedScope[
|
|
115
|
+
if (defScopes.includes(analyzedScope[SCOPE_INDEX])) {
|
|
116
|
+
if (analyzedScope[CLAIMS_INDEX]) {
|
|
101
117
|
const includedDefinitionName = `${analyzedResource[resourceIndex]}${CLAIMS_DEFINITION}`;
|
|
102
118
|
|
|
103
119
|
if (!definition.components || !definition.components.schemas) {
|
|
104
|
-
throw getError(`missing ${includedDefinitionName} definition: no
|
|
120
|
+
throw getError(`missing ${includedDefinitionName} definition: no definition`, SYSTEM_ERROR);
|
|
105
121
|
}
|
|
106
122
|
const includedDefinition = definition.components.schemas[includedDefinitionName];
|
|
107
123
|
|
|
108
124
|
if (!includedDefinition) {
|
|
109
|
-
throw getError(`missing ${includedDefinitionName} definition`,
|
|
125
|
+
throw getError(`missing ${includedDefinitionName} definition`, SYSTEM_ERROR);
|
|
110
126
|
}
|
|
111
|
-
const includedClaims = analyzedScope[
|
|
127
|
+
const includedClaims = analyzedScope[CLAIMS_INDEX].split(CLAIMS_SEPARATOR);
|
|
112
128
|
const definitionClaims = Object.keys(includedDefinition);
|
|
113
129
|
const claimsIntersects = intersection(includedClaims, definitionClaims);
|
|
114
130
|
|
|
115
131
|
if (claimsIntersects.length !== includedClaims.length) {
|
|
116
|
-
throw getError(`incorrect claims included: ${difference(includedClaims, claimsIntersects)}`,
|
|
132
|
+
throw getError(`incorrect claims included: ${difference(includedClaims, claimsIntersects)}`, FORBIDDEN_ERROR);
|
|
117
133
|
}
|
|
118
134
|
claims = claims.concat(claimsIntersects);
|
|
119
135
|
}
|
|
120
|
-
intersects.push(analyzedScope[
|
|
136
|
+
intersects.push(analyzedScope[SCOPE_INDEX]);
|
|
121
137
|
}
|
|
122
138
|
});
|
|
123
|
-
if (intersects.length ===
|
|
124
|
-
throw getError(`incorrect scopes: ${tokenScopes}`,
|
|
139
|
+
if (intersects.length === EMPTY) {
|
|
140
|
+
throw getError(`incorrect scopes: ${tokenScopes}`, FORBIDDEN_ERROR);
|
|
125
141
|
}
|
|
126
142
|
}
|
|
127
143
|
return { onBehalf, claims };
|
|
128
144
|
};
|
|
129
145
|
|
|
130
|
-
|
|
146
|
+
export const securityLib = (config) => {
|
|
131
147
|
const verifyTokenClientCredentials = (authToken) => {
|
|
132
148
|
const { server, generic } = config.security;
|
|
133
149
|
const options = {
|
|
@@ -136,17 +152,21 @@ module.exports = (config) => {
|
|
|
136
152
|
// subject: `${config.serverSettings.id}@clients`,
|
|
137
153
|
};
|
|
138
154
|
|
|
139
|
-
try {
|
|
155
|
+
try {
|
|
156
|
+
jwt.verify(authToken, (generic.key === NO_GENERIC) ? server.accessKey : generic.key, options);
|
|
157
|
+
}
|
|
140
158
|
catch (err) {
|
|
141
159
|
if (generic.previousKey) { // backward compatibility
|
|
142
|
-
try {
|
|
160
|
+
try {
|
|
161
|
+
jwt.verify(authToken, generic.previousKey, options);
|
|
162
|
+
}
|
|
143
163
|
catch (secondErr) {
|
|
144
|
-
secondErr.statusCode =
|
|
164
|
+
secondErr.statusCode = FORBIDDEN_ERROR;
|
|
145
165
|
throw secondErr;
|
|
146
166
|
}
|
|
147
167
|
}
|
|
148
168
|
else {
|
|
149
|
-
err.statusCode =
|
|
169
|
+
err.statusCode = FORBIDDEN_ERROR;
|
|
150
170
|
throw err;
|
|
151
171
|
}
|
|
152
172
|
}
|
|
@@ -159,32 +179,34 @@ module.exports = (config) => {
|
|
|
159
179
|
issuer: (implicit && implicit.issuer) || server.issuer,
|
|
160
180
|
};
|
|
161
181
|
|
|
162
|
-
try {
|
|
182
|
+
try {
|
|
183
|
+
jwt.verify(authToken, (implicit && implicit.key) || ((generic.key === NO_GENERIC) ? server.accessKey : generic.key), options);
|
|
184
|
+
}
|
|
163
185
|
catch (err) {
|
|
164
|
-
err.statusCode =
|
|
186
|
+
err.statusCode = FORBIDDEN_ERROR;
|
|
165
187
|
throw err;
|
|
166
188
|
}
|
|
167
189
|
};
|
|
168
190
|
|
|
169
191
|
const AdminSecurity = {
|
|
170
|
-
regular: (
|
|
192
|
+
regular: (con, req) => {
|
|
171
193
|
const authToken = checkHeaders(req.headers);
|
|
172
194
|
const token = checkToken(authToken);
|
|
173
|
-
const { request } =
|
|
195
|
+
const { request } = con;
|
|
174
196
|
|
|
175
197
|
if (token.subType !== ADMIN && token.subType !== SUB_ADMIN) {
|
|
176
|
-
throw getError('invalid token: wrong type',
|
|
198
|
+
throw getError('invalid token: wrong type', FORBIDDEN_ERROR);
|
|
177
199
|
}
|
|
178
200
|
if (token.subType === SUB_ADMIN) {
|
|
179
201
|
if (!token.cust) {
|
|
180
|
-
throw getError('invalid token: no customer',
|
|
202
|
+
throw getError('invalid token: no customer', FORBIDDEN_ERROR);
|
|
181
203
|
}
|
|
182
204
|
}
|
|
183
205
|
else if (token.sub !== `${config.security.admin.externalId}${CLIENT}`) {
|
|
184
|
-
throw getError(`jwt subject invalid: ${token.sub}`,
|
|
206
|
+
throw getError(`jwt subject invalid: ${token.sub}`, FORBIDDEN_ERROR);
|
|
185
207
|
}
|
|
186
208
|
verifyTokenClientCredentials(authToken);
|
|
187
|
-
const scopeResult = checkScopes(token.scope, getScopes(
|
|
209
|
+
const scopeResult = checkScopes(token.scope, getScopes(con, ADMIN_SECURITY), con.api.definition);
|
|
188
210
|
|
|
189
211
|
req[TOKEN_PARAMS.claims] = scopeResult.claims;
|
|
190
212
|
request[TOKEN_PARAMS.claims] = scopeResult.claims;
|
|
@@ -202,8 +224,8 @@ module.exports = (config) => {
|
|
|
202
224
|
}
|
|
203
225
|
return true;
|
|
204
226
|
},
|
|
205
|
-
mock: (
|
|
206
|
-
const { request } =
|
|
227
|
+
mock: (con, req) => {
|
|
228
|
+
const { request } = con;
|
|
207
229
|
|
|
208
230
|
req[TOKEN_PARAMS.claims] = ['dummyClaims'];
|
|
209
231
|
req[TOKEN_PARAMS.tokenType] = ADMIN;
|
|
@@ -218,16 +240,16 @@ module.exports = (config) => {
|
|
|
218
240
|
};
|
|
219
241
|
|
|
220
242
|
const SystemSecurity = {
|
|
221
|
-
regular: (
|
|
243
|
+
regular: (con, req) => {
|
|
222
244
|
const authToken = checkHeaders(req.headers);
|
|
223
245
|
const token = checkToken(authToken);
|
|
224
|
-
const { request } =
|
|
246
|
+
const { request } = con;
|
|
225
247
|
|
|
226
248
|
if (token.subType === ADMIN || token.subType === SUB_ADMIN) {
|
|
227
|
-
throw getError('invalid token: wrong type',
|
|
249
|
+
throw getError('invalid token: wrong type', FORBIDDEN_ERROR);
|
|
228
250
|
}
|
|
229
251
|
verifyTokenClientCredentials(authToken);
|
|
230
|
-
const scopeResult = checkScopes(token.scope, getScopes(
|
|
252
|
+
const scopeResult = checkScopes(token.scope, getScopes(con, SYSTEM_SECURITY), con.api.definition);
|
|
231
253
|
|
|
232
254
|
if (scopeResult.onBehalf) {
|
|
233
255
|
req[TOKEN_PARAMS.onBehalf] = true;
|
|
@@ -253,8 +275,8 @@ module.exports = (config) => {
|
|
|
253
275
|
}
|
|
254
276
|
return true;
|
|
255
277
|
},
|
|
256
|
-
mock: (
|
|
257
|
-
const { request } =
|
|
278
|
+
mock: (con, req) => {
|
|
279
|
+
const { request } = con;
|
|
258
280
|
|
|
259
281
|
req[TOKEN_PARAMS.claims] = ['dummyClaims'];
|
|
260
282
|
req[TOKEN_PARAMS.tokenType] = 'dummyServiceType';
|
|
@@ -269,13 +291,13 @@ module.exports = (config) => {
|
|
|
269
291
|
};
|
|
270
292
|
|
|
271
293
|
const UserSecurity = {
|
|
272
|
-
regular: (
|
|
294
|
+
regular: (con, req) => {
|
|
273
295
|
const authToken = checkHeaders(req.headers);
|
|
274
296
|
const token = checkToken(authToken);
|
|
275
|
-
const { request } =
|
|
297
|
+
const { request } = con;
|
|
276
298
|
|
|
277
299
|
verifyTokenImplicit(authToken);
|
|
278
|
-
const scopeResult = checkScopes(token.scope, getScopes(
|
|
300
|
+
const scopeResult = checkScopes(token.scope, getScopes(con, USER_SECURITY), con.api.definition);
|
|
279
301
|
|
|
280
302
|
if (scopeResult.onBehalf) {
|
|
281
303
|
req[TOKEN_PARAMS.onBehalf] = true;
|
|
@@ -301,8 +323,8 @@ module.exports = (config) => {
|
|
|
301
323
|
}
|
|
302
324
|
return true;
|
|
303
325
|
},
|
|
304
|
-
mock: (
|
|
305
|
-
const { request } =
|
|
326
|
+
mock: (con, req) => {
|
|
327
|
+
const { request } = con;
|
|
306
328
|
|
|
307
329
|
req[TOKEN_PARAMS.claims] = ['dummyClaims'];
|
|
308
330
|
req[TOKEN_PARAMS.userId] = 'dummyUserId';
|
|
@@ -317,19 +339,19 @@ module.exports = (config) => {
|
|
|
317
339
|
};
|
|
318
340
|
|
|
319
341
|
const ApiKeySecurity = {
|
|
320
|
-
regular: (
|
|
342
|
+
regular: (con, req) => {
|
|
321
343
|
const apiKey = req.headers ? req.headers[API_KEY_NAME.toLowerCase()] : null;
|
|
322
|
-
const { request } =
|
|
344
|
+
const { request } = con;
|
|
323
345
|
|
|
324
346
|
if (config.security.apiKeys.includes(apiKey)) {
|
|
325
347
|
req[API_KEY_NAME] = apiKey;
|
|
326
348
|
request[API_KEY_NAME] = apiKey;
|
|
327
349
|
return true;
|
|
328
350
|
}
|
|
329
|
-
throw getError('invalid API key',
|
|
351
|
+
throw getError('invalid API key', UNAUTHORIZED_ERROR);
|
|
330
352
|
},
|
|
331
|
-
mock: (
|
|
332
|
-
const { request } =
|
|
353
|
+
mock: (con, req) => {
|
|
354
|
+
const { request } = con;
|
|
333
355
|
|
|
334
356
|
req[API_KEY_NAME] = 'dummyApiKey';
|
|
335
357
|
request[API_KEY_NAME] = 'dummyApiKey';
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mimik/api-helper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "helper for openAPI backend and mimik service",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"lint": "eslint --
|
|
8
|
+
"lint": "eslint . --no-error-on-unmatched-pattern",
|
|
8
9
|
"docs": "jsdoc2md index.js > README.md",
|
|
9
10
|
"test": "echo \"Error: no test specified\" && exit 0",
|
|
10
11
|
"test-ci": "echo \"Error: no test specified\" && exit 0",
|
|
11
12
|
"prepublishOnly": "npm run docs && npm run lint && npm run test-ci",
|
|
12
|
-
"commit-ready": "npm run docs && npm run lint && npm run test-ci"
|
|
13
|
-
"prepare": "husky install"
|
|
13
|
+
"commit-ready": "npm run docs && npm run lint && npm run test-ci"
|
|
14
14
|
},
|
|
15
15
|
"husky": {
|
|
16
16
|
"hooks": {
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"url": "https://bitbucket.org/mimiktech/api-helper"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@mimik/request-helper":"^
|
|
34
|
-
"@mimik/request-retry": "^
|
|
35
|
-
"@mimik/response-helper": "^
|
|
36
|
-
"@mimik/sumologic-winston-logger": "^
|
|
37
|
-
"@mimik/swagger-helper": "^
|
|
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",
|
|
38
38
|
"ajv-formats": "3.0.1",
|
|
39
39
|
"js-base64": "3.7.7",
|
|
40
40
|
"js-yaml":"4.1.0",
|
|
@@ -42,19 +42,16 @@
|
|
|
42
42
|
"lodash.compact": "3.0.1",
|
|
43
43
|
"lodash.difference": "4.5.0",
|
|
44
44
|
"lodash.intersection": "4.4.0",
|
|
45
|
-
"openapi-backend": "5.
|
|
46
|
-
"swagger-client": "3.
|
|
45
|
+
"openapi-backend": "5.13.0",
|
|
46
|
+
"swagger-client": "3.35.6"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@
|
|
50
|
-
"@mimik/eslint-plugin-document-env": "^
|
|
51
|
-
"eslint": "
|
|
52
|
-
"eslint
|
|
53
|
-
"eslint-plugin-import": "2.
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"eslint-plugin-react-hooks": "4.6.2",
|
|
57
|
-
"husky": "9.1.6",
|
|
58
|
-
"jsdoc-to-markdown": "9.0.2"
|
|
49
|
+
"@eslint/js": "9.31.0",
|
|
50
|
+
"@mimik/eslint-plugin-document-env": "^2.0.8",
|
|
51
|
+
"@stylistic/eslint-plugin": "5.2.1",
|
|
52
|
+
"eslint": "9.31.0",
|
|
53
|
+
"eslint-plugin-import": "2.32.0",
|
|
54
|
+
"husky": "9.1.7",
|
|
55
|
+
"jsdoc-to-markdown": "9.1.2"
|
|
59
56
|
}
|
|
60
57
|
}
|
package/.eslintrc
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"plugins": [
|
|
3
|
-
"@mimik/document-env",
|
|
4
|
-
"@mimik/dependencies"
|
|
5
|
-
],
|
|
6
|
-
"env": {
|
|
7
|
-
"node": true
|
|
8
|
-
},
|
|
9
|
-
"parserOptions": {
|
|
10
|
-
"ecmaVersion": 2020
|
|
11
|
-
},
|
|
12
|
-
"extends": "airbnb",
|
|
13
|
-
"rules": {
|
|
14
|
-
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }],
|
|
15
|
-
"import/no-unresolved": ["error", { "amd": true, "commonjs": true, "caseSensitiveStrict": true }],
|
|
16
|
-
"brace-style": [1, "stroustrup", { "allowSingleLine": true }],
|
|
17
|
-
"no-confusing-arrow": [0], // arrow isnt confusing
|
|
18
|
-
"max-len": [1, 180, { "ignoreComments": true }],
|
|
19
|
-
"linebreak-style": 0,
|
|
20
|
-
"quotes": [1, "single"],
|
|
21
|
-
"semi": [1, "always"],
|
|
22
|
-
"no-process-env": ["error"],
|
|
23
|
-
"@mimik/document-env/validate-document-env": 2,
|
|
24
|
-
"@mimik/dependencies/case-sensitive": 2,
|
|
25
|
-
"@mimik/dependencies/no-cycles": 2,
|
|
26
|
-
"@mimik/dependencies/require-json-ext": 2
|
|
27
|
-
},
|
|
28
|
-
"settings":{
|
|
29
|
-
"react": {
|
|
30
|
-
"version": "detect"
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"globals": {
|
|
34
|
-
"module": true,
|
|
35
|
-
"require": true,
|
|
36
|
-
"const": false,
|
|
37
|
-
"it": false,
|
|
38
|
-
"describe": false,
|
|
39
|
-
"before": true,
|
|
40
|
-
"after": true,
|
|
41
|
-
"JSON": true
|
|
42
|
-
}
|
|
43
|
-
}
|