@readme/oas-to-har 15.0.0 → 17.0.0
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/CHANGELOG.md +42 -0
- package/LICENSE +1 -1
- package/README.md +28 -12
- package/karma.conf.js +14 -0
- package/package.json +22 -14
- package/src/index.js +45 -33
- package/src/lib/style-formatting/index.js +90 -36
- package/src/lib/style-formatting/style-serializer.js +218 -106
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -7
- package/.github/dependabot.yml +0 -26
- package/.github/workflows/ci.yml +0 -24
- package/.github/workflows/codeql-analysis.yml +0 -35
- package/.husky/commit-msg +0 -4
- package/CODE_OF_CONDUCT.md +0 -128
- package/SECURITY.md +0 -12
- package/src/lib/remove-undefined-objects.js +0 -56
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
## 17.0.0 (2022-04-22)
|
|
2
|
+
|
|
3
|
+
> **BREAKING RELEASE**
|
|
4
|
+
>
|
|
5
|
+
> Node 12 is no longer supported.
|
|
6
|
+
|
|
7
|
+
* chore: adding some more exclusions into the npmignore list ([25b3121](https://github.com/readmeio/oas-to-har/commit/25b3121))
|
|
8
|
+
* chore(deps-dev): bump @readme/oas-examples from 4.5.0 to 5.0.0 (#75) ([845535b](https://github.com/readmeio/oas-to-har/commit/845535b)), closes [#75](https://github.com/readmeio/oas-to-har/issues/75)
|
|
9
|
+
* chore(deps-dev): bump eslint from 8.11.0 to 8.12.0 (#73) ([c26d37c](https://github.com/readmeio/oas-to-har/commit/c26d37c)), closes [#73](https://github.com/readmeio/oas-to-har/issues/73)
|
|
10
|
+
* chore(deps-dev): bump prettier from 2.6.0 to 2.6.1 (#72) ([24613fc](https://github.com/readmeio/oas-to-har/commit/24613fc)), closes [#72](https://github.com/readmeio/oas-to-har/issues/72)
|
|
11
|
+
* chore(deps): bump actions/checkout from 2.4.0 to 3 (#71) ([38c66a7](https://github.com/readmeio/oas-to-har/commit/38c66a7)), closes [#71](https://github.com/readmeio/oas-to-har/issues/71)
|
|
12
|
+
* chore(deps): bump oas from 18.0.6 to 18.1.0 (#74) ([93c2bc4](https://github.com/readmeio/oas-to-har/commit/93c2bc4)), closes [#74](https://github.com/readmeio/oas-to-har/issues/74)
|
|
13
|
+
* fix: code cleanup (#79) ([245dcbb](https://github.com/readmeio/oas-to-har/commit/245dcbb)), closes [#79](https://github.com/readmeio/oas-to-har/issues/79)
|
|
14
|
+
* fix: support for allowReserved (#76) ([f609297](https://github.com/readmeio/oas-to-har/commit/f609297)), closes [#76](https://github.com/readmeio/oas-to-har/issues/76)
|
|
15
|
+
* fix: support for multi-level deepObjects (#77) ([b287f68](https://github.com/readmeio/oas-to-har/commit/b287f68)), closes [#77](https://github.com/readmeio/oas-to-har/issues/77)
|
|
16
|
+
* feat: browser testing! (#78) ([5ebe4d4](https://github.com/readmeio/oas-to-har/commit/5ebe4d4)), closes [#78](https://github.com/readmeio/oas-to-har/issues/78)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## 16.1.0 (2022-03-23)
|
|
21
|
+
|
|
22
|
+
* chore: removing some docs from the repo as they're in our .github/ repo now ([f3d81c3](https://github.com/readmeio/oas-to-har/commit/f3d81c3))
|
|
23
|
+
* chore(deps): bumping out of date deps ([0d7add6](https://github.com/readmeio/oas-to-har/commit/0d7add6))
|
|
24
|
+
* fix: issue where null-assigned object properties would be filtered out (#70) ([9d51794](https://github.com/readmeio/oas-to-har/commit/9d51794)), closes [#70](https://github.com/readmeio/oas-to-har/issues/70)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## 16.0.0 (2022-03-08)
|
|
29
|
+
|
|
30
|
+
* chore(deps-dev): bump @commitlint/cli from 16.1.0 to 16.2.1 (#65) ([d1ce5b5](https://github.com/readmeio/oas-to-har/commit/d1ce5b5)), closes [#65](https://github.com/readmeio/oas-to-har/issues/65)
|
|
31
|
+
* chore(deps-dev): bump @commitlint/config-conventional (#62) ([51ad7f3](https://github.com/readmeio/oas-to-har/commit/51ad7f3)), closes [#62](https://github.com/readmeio/oas-to-har/issues/62)
|
|
32
|
+
* chore(deps-dev): bump @readme/eslint-config from 8.2.0 to 8.5.0 (#67) ([adae50c](https://github.com/readmeio/oas-to-har/commit/adae50c)), closes [#67](https://github.com/readmeio/oas-to-har/issues/67)
|
|
33
|
+
* chore(deps-dev): bump eslint from 8.8.0 to 8.10.0 (#63) ([31c1aa3](https://github.com/readmeio/oas-to-har/commit/31c1aa3)), closes [#63](https://github.com/readmeio/oas-to-har/issues/63)
|
|
34
|
+
* chore(deps-dev): bump jest from 27.4.7 to 27.5.1 (#64) ([d99e123](https://github.com/readmeio/oas-to-har/commit/d99e123)), closes [#64](https://github.com/readmeio/oas-to-har/issues/64)
|
|
35
|
+
* chore(deps): bump actions/setup-node from 2.5.1 to 3 (#61) ([362859e](https://github.com/readmeio/oas-to-har/commit/362859e)), closes [#61](https://github.com/readmeio/oas-to-har/issues/61)
|
|
36
|
+
* chore(deps): upgrading oas to v18 (#69) ([3a5a54d](https://github.com/readmeio/oas-to-har/commit/3a5a54d)), closes [#69](https://github.com/readmeio/oas-to-har/issues/69)
|
|
37
|
+
* docs: incorporating alex into our documentation workflow (#68) ([6b20368](https://github.com/readmeio/oas-to-har/commit/6b20368)), closes [#68](https://github.com/readmeio/oas-to-har/issues/68)
|
|
38
|
+
* feat: moving to our new remove-undefined-objects package (#59) ([d35a014](https://github.com/readmeio/oas-to-har/commit/d35a014)), closes [#59](https://github.com/readmeio/oas-to-har/issues/59)
|
|
39
|
+
* feat(styles): add space and pipe delimited support for objects per 3.1 (#60) ([49e2d13](https://github.com/readmeio/oas-to-har/commit/49e2d13)), closes [#60](https://github.com/readmeio/oas-to-har/issues/60)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
1
43
|
## 15.0.0 (2022-02-03)
|
|
2
44
|
|
|
3
45
|
> **BREAKING RELEASE**
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Utility to transform an OAS operation into a [HAR](http://www.softwareishard.com/blog/har-12-spec/) representation
|
|
4
4
|
|
|
5
|
-
[](https://github.com/readmeio/oas-to-har)
|
|
5
|
+
[](https://github.com/readmeio/oas-to-har/) [](https://npm.im/@readme/oas-to-har)
|
|
6
6
|
|
|
7
7
|
[](https://readme.io)
|
|
8
8
|
|
|
@@ -15,14 +15,37 @@ npm install --save @readme/oas-to-har
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
|
-
const Oas = require('oas');
|
|
18
|
+
const Oas = require('oas').default;
|
|
19
19
|
const oasToHar = require('@readme/oas-to-har');
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
const petstore = require('./petstore.json');
|
|
22
|
+
|
|
23
|
+
const spec = new Oas(petstore);
|
|
24
|
+
console.log(oasToHar(spec, spec.operation('/pets', 'post')));
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
log: {
|
|
30
|
+
entries: [
|
|
31
|
+
{
|
|
32
|
+
request: {
|
|
33
|
+
cookies: [],
|
|
34
|
+
headers: [],
|
|
35
|
+
headersSize: 0,
|
|
36
|
+
queryString: [],
|
|
37
|
+
bodySize: 0,
|
|
38
|
+
method: 'POST',
|
|
39
|
+
url: 'http://petstore.swagger.io/v2/pets',
|
|
40
|
+
httpVersion: 'HTTP/1.1'
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
23
46
|
```
|
|
24
47
|
|
|
25
|
-
### `oasToHar(
|
|
48
|
+
### `oasToHar(oas, operationSchema, values, auth, opts) => Object`
|
|
26
49
|
|
|
27
50
|
- `oas` *{Oas}*: Instance of our [oas/tooling](https://npm.im/oas) class.
|
|
28
51
|
- `operationSchema` *{Object\|Operation}*: Can either be an object with `path` and `method` properties (that exist in the supplied OAS), or an instance of our `Operation` class from [oas/tooling](https://npm.im/oas) - accessed through `new Oas(spec).operation(path, method)`.
|
|
@@ -36,10 +59,3 @@ console.log(oasToHar(spec, { path: '/pets', method: 'post'}));
|
|
|
36
59
|
- `server` — If the supplied OAS has multiple severs or server variables you can use this to set which server and variables to use. Shape of it should be: `{ selected: Integer, variables: { ...key-values }}`. `selected` should coorespond to index of the `servers` array in your OAS.
|
|
37
60
|
- `auth` *{Object}*: Authentication information for the request.
|
|
38
61
|
- `opts.proxyUrl` *{Boolean}*: Boolean to toggle if composed HAR objects should have their `url` be sent through our CORS-friendly proxy. Defaults to `false`.
|
|
39
|
-
|
|
40
|
-
## Credits
|
|
41
|
-
[Jon Ursenbach](https://github.com/erunion)
|
|
42
|
-
|
|
43
|
-
## License
|
|
44
|
-
|
|
45
|
-
ISC
|
package/karma.conf.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { karmaConfig } = require('@jsdevtools/karma-config');
|
|
2
|
+
const { host } = require('@jsdevtools/host-environment');
|
|
3
|
+
|
|
4
|
+
module.exports = karmaConfig({
|
|
5
|
+
sourceDir: '.',
|
|
6
|
+
fixtures: ['test/__datasets__/*.json'],
|
|
7
|
+
browsers: {
|
|
8
|
+
chrome: true,
|
|
9
|
+
firefox: true,
|
|
10
|
+
safari: host.os.mac,
|
|
11
|
+
edge: false,
|
|
12
|
+
ie: false,
|
|
13
|
+
},
|
|
14
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@readme/oas-to-har",
|
|
3
3
|
"description": "Utility to transform an OAS operation into a HAR representation",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "17.0.0",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"author": "Jon Ursenbach <jon@ursenba.ch>",
|
|
7
7
|
"license": "ISC",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"url": "git://github.com/readmeio/oas-to-har.git"
|
|
11
11
|
},
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": "
|
|
13
|
+
"node": ">=14"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"lint": "eslint .",
|
|
@@ -18,24 +18,32 @@
|
|
|
18
18
|
"pretest": "npm run lint",
|
|
19
19
|
"prettier": "prettier --list-different --write \"./**/**.{js}\"",
|
|
20
20
|
"release": "npx conventional-changelog-cli -i CHANGELOG.md -s && git add CHANGELOG.md",
|
|
21
|
-
"test": "
|
|
21
|
+
"test:browser": "karma start --single-run",
|
|
22
|
+
"test:browser:chrome": "karma start --browsers=Chrome --single-run=false",
|
|
23
|
+
"test:browser:debug": "karma start --single-run=false",
|
|
24
|
+
"test": "nyc mocha"
|
|
22
25
|
},
|
|
23
26
|
"dependencies": {
|
|
24
|
-
"@readme/oas-extensions": "^14.
|
|
25
|
-
"oas": "^
|
|
26
|
-
"parse-data-url": "^4.0.1"
|
|
27
|
+
"@readme/oas-extensions": "^14.2.0",
|
|
28
|
+
"oas": "^18.0.6",
|
|
29
|
+
"parse-data-url": "^4.0.1",
|
|
30
|
+
"qs": "^6.10.3",
|
|
31
|
+
"remove-undefined-objects": "^1.1.0"
|
|
27
32
|
},
|
|
28
33
|
"devDependencies": {
|
|
29
|
-
"@commitlint/cli": "^16.
|
|
34
|
+
"@commitlint/cli": "^16.2.3",
|
|
30
35
|
"@commitlint/config-conventional": "^16.0.0",
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
33
|
-
"
|
|
34
|
-
"
|
|
36
|
+
"@jsdevtools/host-environment": "^2.1.2",
|
|
37
|
+
"@jsdevtools/karma-config": "^3.1.7",
|
|
38
|
+
"@readme/eslint-config": "^8.5.1",
|
|
39
|
+
"@readme/oas-examples": "^5.0.0",
|
|
40
|
+
"chai": "^4.3.6",
|
|
41
|
+
"eslint": "^8.11.0",
|
|
42
|
+
"har-validator": "^5.1.5",
|
|
35
43
|
"husky": "^7.0.4",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"prettier": "^2.
|
|
44
|
+
"mocha": "^9.2.2",
|
|
45
|
+
"nyc": "^15.1.0",
|
|
46
|
+
"prettier": "^2.6.0"
|
|
39
47
|
},
|
|
40
48
|
"prettier": "@readme/eslint-config/prettier",
|
|
41
49
|
"commitlint": {
|
package/src/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const extensions = require('@readme/oas-extensions');
|
|
2
2
|
const { Operation, utils } = require('oas');
|
|
3
3
|
const parseDataUrl = require('parse-data-url');
|
|
4
|
+
const { default: removeUndefinedObjects } = require('remove-undefined-objects');
|
|
4
5
|
|
|
5
6
|
const configureSecurity = require('./lib/configure-security');
|
|
6
|
-
const removeUndefinedObjects = require('./lib/remove-undefined-objects');
|
|
7
7
|
const formatStyle = require('./lib/style-formatting');
|
|
8
8
|
|
|
9
9
|
const { jsonSchemaTypes } = utils;
|
|
@@ -11,8 +11,8 @@ const { jsonSchemaTypes } = utils;
|
|
|
11
11
|
function formatter(values, param, type, onlyIfExists) {
|
|
12
12
|
if (param.style) {
|
|
13
13
|
const value = values[type][param.name];
|
|
14
|
-
// Note: Technically we could send everything through the format style and choose the proper
|
|
15
|
-
//
|
|
14
|
+
// Note: Technically we could send everything through the format style and choose the proper
|
|
15
|
+
// default for each `in` type (e.g. query defaults to form).
|
|
16
16
|
return formatStyle(value, param);
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -26,18 +26,20 @@ function formatter(values, param, type, onlyIfExists) {
|
|
|
26
26
|
} else if (param.required && param.schema && param.schema.default) {
|
|
27
27
|
value = param.schema.default;
|
|
28
28
|
} else if (type === 'path') {
|
|
29
|
-
// If we don't have any values for the path parameter, just use the name of the parameter as the
|
|
30
|
-
// try try to build a URL to something like `https://example.com/undefined`.
|
|
29
|
+
// If we don't have any values for the path parameter, just use the name of the parameter as the
|
|
30
|
+
// value so we don't try try to build a URL to something like `https://example.com/undefined`.
|
|
31
31
|
return param.name;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// Handle file uploads. Specifically arrays of file uploads which need to be formatted very
|
|
34
|
+
// Handle file uploads. Specifically arrays of file uploads which need to be formatted very
|
|
35
|
+
// specifically.
|
|
35
36
|
if (param.schema && param.schema.type === 'array' && param.schema.items && param.schema.items.format === 'binary') {
|
|
36
37
|
return JSON.stringify(value);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
if (value !== undefined) {
|
|
40
|
-
// Query params should always be formatted, even if they don't have a `style` serialization
|
|
41
|
+
// Query params should always be formatted, even if they don't have a `style` serialization
|
|
42
|
+
// configured.
|
|
41
43
|
if (type === 'query') {
|
|
42
44
|
return formatStyle(value, param);
|
|
43
45
|
}
|
|
@@ -77,7 +79,8 @@ function multipartBodyToFormatterParams(multipartBody, oasMediaTypeObject) {
|
|
|
77
79
|
.filter(Boolean);
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
// Pretty sure that we'll never have anything but an object for multipart bodies, so returning
|
|
82
|
+
// Pretty sure that we'll never have anything but an object for multipart bodies, so returning
|
|
83
|
+
// empty array if we get anything else.
|
|
81
84
|
return [];
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -118,12 +121,14 @@ function appendHarValue(harParam, name, value, addtlData = {}) {
|
|
|
118
121
|
if (typeof value === 'undefined') return;
|
|
119
122
|
|
|
120
123
|
if (Array.isArray(value)) {
|
|
121
|
-
// If the formatter gives us an array, we're expected to add each array value as a new
|
|
124
|
+
// If the formatter gives us an array, we're expected to add each array value as a new
|
|
125
|
+
// parameter item with the same parameter name
|
|
122
126
|
value.forEach(singleValue => {
|
|
123
127
|
appendHarValue(harParam, name, singleValue);
|
|
124
128
|
});
|
|
125
129
|
} else if (typeof value === 'object' && value !== null) {
|
|
126
|
-
// If the formatter gives us an object, we're expected to add each property value as a new
|
|
130
|
+
// If the formatter gives us an object, we're expected to add each property value as a new
|
|
131
|
+
// parameter item, each with the name of the property
|
|
127
132
|
Object.keys(value).forEach(key => {
|
|
128
133
|
appendHarValue(harParam, key, value[key]);
|
|
129
134
|
});
|
|
@@ -143,8 +148,8 @@ module.exports = (
|
|
|
143
148
|
values = {},
|
|
144
149
|
auth = {},
|
|
145
150
|
opts = {
|
|
146
|
-
// If true, the operation URL will be rewritten and prefixed with https://try.readme.io/ in
|
|
147
|
-
// through our CORS-friendly proxy.
|
|
151
|
+
// If true, the operation URL will be rewritten and prefixed with https://try.readme.io/ in
|
|
152
|
+
// order to funnel requests through our CORS-friendly proxy.
|
|
148
153
|
proxyUrl: false,
|
|
149
154
|
}
|
|
150
155
|
) => {
|
|
@@ -196,7 +201,8 @@ module.exports = (
|
|
|
196
201
|
// Find the path parameter or set a default value if it does not exist
|
|
197
202
|
const parameter = parameters.find(param => param.name === key) || { name: key };
|
|
198
203
|
|
|
199
|
-
// The library that handles our style processing already encodes uri elements. For everything
|
|
204
|
+
// The library that handles our style processing already encodes uri elements. For everything
|
|
205
|
+
// else we need to handle it here.
|
|
200
206
|
if (!parameter.style) {
|
|
201
207
|
return encodeURIComponent(formatter(formData, parameter, 'path'));
|
|
202
208
|
}
|
|
@@ -316,8 +322,9 @@ module.exports = (
|
|
|
316
322
|
har.postData.mimeType = 'multipart/form-data';
|
|
317
323
|
har.postData.params = [];
|
|
318
324
|
|
|
319
|
-
// Discover all `{ type: string, format: binary }` properties the schema. If there are
|
|
320
|
-
// that we're dealing with a `multipart/form-data` request and
|
|
325
|
+
// Discover all `{ type: string, format: binary }` properties the schema. If there are
|
|
326
|
+
// any, then that means that we're dealing with a `multipart/form-data` request and
|
|
327
|
+
// need to treat the payload as `postData.params`.
|
|
321
328
|
const binaryTypes = Object.keys(requestBody.schema.properties).filter(
|
|
322
329
|
key => requestBody.schema.properties[key].format === 'binary'
|
|
323
330
|
);
|
|
@@ -333,9 +340,10 @@ module.exports = (
|
|
|
333
340
|
|
|
334
341
|
const value = formatter(formData, param, 'body', true);
|
|
335
342
|
|
|
336
|
-
// If we're dealing with a binary type, and the value is a valid data URL we should
|
|
337
|
-
// available filename and content type to send along with the
|
|
338
|
-
// can make sense of it and send a usable
|
|
343
|
+
// If we're dealing with a binary type, and the value is a valid data URL we should
|
|
344
|
+
// parse out any available filename and content type to send along with the
|
|
345
|
+
// parameter to interpreters like `fetch-har` can make sense of it and send a usable
|
|
346
|
+
// payload.
|
|
339
347
|
const addtlData = {};
|
|
340
348
|
|
|
341
349
|
if (binaryTypes.includes(name)) {
|
|
@@ -355,17 +363,21 @@ module.exports = (
|
|
|
355
363
|
har.postData.mimeType = contentType;
|
|
356
364
|
|
|
357
365
|
// Handle arbitrary JSON input via a string.
|
|
358
|
-
//
|
|
359
|
-
//
|
|
360
|
-
// In the UI this is represented by an arbitrary text input
|
|
361
|
-
//
|
|
366
|
+
//
|
|
367
|
+
// In OAS you usually find this in an `application/json` content type with a schema
|
|
368
|
+
// `type=string, format=json`. In the UI this is represented by an arbitrary text input.
|
|
369
|
+
//
|
|
370
|
+
// This ensures we remove any newlines or tabs and use a clean JSON block in the
|
|
371
|
+
// example.
|
|
362
372
|
if (requestBody.schema.type === 'string') {
|
|
363
373
|
har.postData.text = JSON.stringify(JSON.parse(cleanBody));
|
|
364
374
|
} else {
|
|
365
|
-
// Handle formatted JSON objects that have properties that accept arbitrary JSON
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
//
|
|
375
|
+
// Handle formatted JSON objects that have properties that accept arbitrary JSON.
|
|
376
|
+
//
|
|
377
|
+
// Find all `{ type: string, format: json }` properties in the schema because we need
|
|
378
|
+
// to manually `JSON.parse` them before submit, otherwise they'll be escaped instead
|
|
379
|
+
// of actual objects. We also only want values that the user has entered, so we drop
|
|
380
|
+
// any `undefined` `cleanBody` keys
|
|
369
381
|
const jsonTypes = Object.keys(requestBody.schema.properties).filter(
|
|
370
382
|
key => requestBody.schema.properties[key].format === 'json' && cleanBody[key] !== undefined
|
|
371
383
|
);
|
|
@@ -380,7 +392,8 @@ module.exports = (
|
|
|
380
392
|
}
|
|
381
393
|
});
|
|
382
394
|
|
|
383
|
-
// `RAW_BODY` is a ReadMe-specific thing where we'll interpret its contents as
|
|
395
|
+
// `RAW_BODY` is a ReadMe-specific thing where we'll interpret its contents as
|
|
396
|
+
// raw JSON.
|
|
384
397
|
if (typeof cleanBody.RAW_BODY !== 'undefined') {
|
|
385
398
|
cleanBody = cleanBody.RAW_BODY;
|
|
386
399
|
}
|
|
@@ -395,10 +408,8 @@ module.exports = (
|
|
|
395
408
|
}
|
|
396
409
|
}
|
|
397
410
|
} catch (e) {
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
// If anything above fails for whatever reason, assume that whatever we had is invalid JSON and just treat it
|
|
401
|
-
// as raw text.
|
|
411
|
+
// If anything above fails for whatever reason, assume that whatever we had is invalid
|
|
412
|
+
// JSON and just treat it as raw text.
|
|
402
413
|
har.postData.text = stringify(formData.body);
|
|
403
414
|
}
|
|
404
415
|
} else {
|
|
@@ -412,8 +423,9 @@ module.exports = (
|
|
|
412
423
|
}
|
|
413
424
|
}
|
|
414
425
|
|
|
415
|
-
// Add a `Content-Type` header if there are any body values setup above or if there is a schema
|
|
416
|
-
// so if we don't already have a `Content-Type` present as it's impossible
|
|
426
|
+
// Add a `Content-Type` header if there are any body values setup above or if there is a schema
|
|
427
|
+
// defined, but only do so if we don't already have a `Content-Type` present as it's impossible
|
|
428
|
+
// for a request to have multiple.
|
|
417
429
|
if ((har.postData.text || (requestBody && Object.keys(requestBody.schema).length)) && !hasContentType) {
|
|
418
430
|
har.headers.push({
|
|
419
431
|
name: 'Content-Type',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
const qs = require('qs');
|
|
1
2
|
const stylize = require('./style-serializer');
|
|
2
3
|
|
|
3
|
-
// Certain styles don't support empty values
|
|
4
|
+
// Certain styles don't support empty values.
|
|
4
5
|
function shouldNotStyleEmptyValues(parameter) {
|
|
5
6
|
return ['simple', 'spaceDelimited', 'pipeDelimited', 'deepObject'].includes(parameter.style);
|
|
6
7
|
}
|
|
@@ -9,9 +10,12 @@ function shouldNotStyleReservedHeader(parameter) {
|
|
|
9
10
|
return ['accept', 'authorization', 'content-type'].includes(parameter.name.toLowerCase());
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Note: This isn't necessarily part of the spec. Behavior for the value 'undefined' is, well,
|
|
15
|
+
* undefined. This code makes our system look better. If we wanted to be more accurate, we might
|
|
16
|
+
* want to remove this, restore the un-fixed behavior for undefined and have our UI pass in empty
|
|
17
|
+
* string instead of undefined.
|
|
18
|
+
*/
|
|
15
19
|
function removeUndefinedForPath(value) {
|
|
16
20
|
let finalValue = value;
|
|
17
21
|
|
|
@@ -41,35 +45,43 @@ function stylizeValue(value, parameter) {
|
|
|
41
45
|
|
|
42
46
|
// Some styles don't work with empty values. We catch those there
|
|
43
47
|
if (shouldNotStyleEmptyValues(parameter) && (typeof finalValue === 'undefined' || finalValue === '')) {
|
|
44
|
-
// Paths need return an unstyled empty string instead of undefined so it's ignored in the final
|
|
48
|
+
// Paths need return an unstyled empty string instead of undefined so it's ignored in the final
|
|
49
|
+
// path string.
|
|
45
50
|
if (parameter.in === 'path') {
|
|
46
51
|
return '';
|
|
47
52
|
}
|
|
48
|
-
|
|
53
|
+
|
|
54
|
+
// Everything but path should return undefined when unstyled so it's ignored in the final
|
|
55
|
+
// parameter array.
|
|
49
56
|
return undefined;
|
|
50
57
|
}
|
|
51
58
|
|
|
52
|
-
// Every style that adds their style to empty values should use emptystring for path parameters
|
|
59
|
+
// Every style that adds their style to empty values should use emptystring for path parameters
|
|
60
|
+
// instead of undefined to avoid the string `undefined`.
|
|
53
61
|
if (parameter.in === 'path') {
|
|
54
62
|
finalValue = removeUndefinedForPath(finalValue);
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Eventhough `Accept`, `Authorization`, and `Content-Type` headers can be defined as parameters,
|
|
67
|
+
* they should be completely ignored when it comes to serialization.
|
|
68
|
+
*
|
|
69
|
+
* > If in is "header" and the name field is "Accept", "Content-Type" or "Authorization", the
|
|
70
|
+
* > parameter definition SHALL be ignored.
|
|
71
|
+
*
|
|
72
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#fixed-fields-10}
|
|
73
|
+
*/
|
|
64
74
|
if (parameter.in === 'header' && shouldNotStyleReservedHeader(parameter)) {
|
|
65
75
|
return value;
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
/**
|
|
79
|
+
* All parameter types have a default `style` format so if they don't have one prescribed we
|
|
80
|
+
* should still conform to what the spec defines.
|
|
81
|
+
*
|
|
82
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#user-content-parameterstyle}
|
|
83
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterstyle}
|
|
84
|
+
*/
|
|
73
85
|
let style = parameter.style;
|
|
74
86
|
if (!style) {
|
|
75
87
|
if (parameter.in === 'query') {
|
|
@@ -85,10 +97,12 @@ function stylizeValue(value, parameter) {
|
|
|
85
97
|
|
|
86
98
|
let explode = parameter.explode;
|
|
87
99
|
if (explode === undefined && style === 'form') {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Per the spec if no `explode` is present but `style` is `form` then `explode` should default to `true`.
|
|
102
|
+
*
|
|
103
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#user-content-parameterexplode}
|
|
104
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterexplode}
|
|
105
|
+
*/
|
|
92
106
|
explode = true;
|
|
93
107
|
}
|
|
94
108
|
|
|
@@ -98,15 +112,49 @@ function stylizeValue(value, parameter) {
|
|
|
98
112
|
key: parameter.name,
|
|
99
113
|
style,
|
|
100
114
|
explode,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
115
|
+
/**
|
|
116
|
+
* @todo this parameter is optional to stylize. It defaults to false, and can accept falsy, truthy, or "unsafe".
|
|
117
|
+
* I do not know if it is correct for query to use this. See style-serializer for more info
|
|
118
|
+
*/
|
|
105
119
|
escape: true,
|
|
120
|
+
...(parameter.in === 'query' ? { isAllowedReserved: parameter.allowReserved || false } : {}),
|
|
106
121
|
});
|
|
107
122
|
}
|
|
108
123
|
|
|
109
|
-
|
|
124
|
+
function handleDeepObject(value, parameter) {
|
|
125
|
+
return qs
|
|
126
|
+
.stringify(value, {
|
|
127
|
+
// eslint-disable-next-line consistent-return
|
|
128
|
+
encoder(str, defaultEncoder, charset, type) {
|
|
129
|
+
if (type === 'key') {
|
|
130
|
+
// `str` will be here as `dog[treats][0]` but because the `qs` library doesn't have any
|
|
131
|
+
// awareness of our OpenAPI parameters we need to rewrite it to slap the `parameter.name`
|
|
132
|
+
// to the top, like `pets[dog][treats][0]`.
|
|
133
|
+
const prefixedKey = str
|
|
134
|
+
.split(/[[\]]/g)
|
|
135
|
+
.filter(Boolean)
|
|
136
|
+
.map(k => `[${k}]`)
|
|
137
|
+
.join('');
|
|
138
|
+
|
|
139
|
+
return `${parameter.name}${prefixedKey}`;
|
|
140
|
+
} else if (type === 'value') {
|
|
141
|
+
return stylizeValue(str, parameter);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
.split('&')
|
|
146
|
+
.map(item => {
|
|
147
|
+
const split = item.split('=');
|
|
148
|
+
return {
|
|
149
|
+
label: split[0],
|
|
150
|
+
// `qs` will coerce null values into being `undefined` string but we want to preserve them.
|
|
151
|
+
value: split[1] === 'undefined' ? null : split[1],
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Explode is handled on its own, because style-serializer doesn't return what we expect for proper
|
|
157
|
+
// HAR output.
|
|
110
158
|
function handleExplode(value, parameter) {
|
|
111
159
|
if (Array.isArray(value)) {
|
|
112
160
|
return value.map(val => {
|
|
@@ -118,12 +166,13 @@ function handleExplode(value, parameter) {
|
|
|
118
166
|
const newObj = {};
|
|
119
167
|
|
|
120
168
|
Object.keys(value).forEach(key => {
|
|
121
|
-
const stylizedValue = stylizeValue(value[key], parameter);
|
|
122
|
-
|
|
123
169
|
if (parameter.style === 'deepObject') {
|
|
124
|
-
|
|
170
|
+
const deepObjs = handleDeepObject(value, parameter);
|
|
171
|
+
deepObjs.forEach(obj => {
|
|
172
|
+
newObj[obj.label] = obj.value;
|
|
173
|
+
});
|
|
125
174
|
} else {
|
|
126
|
-
newObj[key] =
|
|
175
|
+
newObj[key] = stylizeValue(value[key], parameter);
|
|
127
176
|
}
|
|
128
177
|
});
|
|
129
178
|
|
|
@@ -148,10 +197,15 @@ module.exports = function formatStyle(value, parameter) {
|
|
|
148
197
|
return undefined;
|
|
149
198
|
}
|
|
150
199
|
|
|
151
|
-
// This custom explode logic allows us to bubble up arrays and objects to be handled differently
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
200
|
+
// This custom explode logic allows us to bubble up arrays and objects to be handled differently
|
|
201
|
+
// by our HAR transformer. We need this because the `stylizeValue` function assumes we're building
|
|
202
|
+
// strings, not richer data types.
|
|
203
|
+
//
|
|
204
|
+
// The first part of this conditional checks if `explode` is enabled. Explode is disabled for
|
|
205
|
+
// everything by default except for forms.
|
|
206
|
+
//
|
|
207
|
+
// The second part of this conditional bypasses the custom explode logic for headers, because they
|
|
208
|
+
// work differently, and `stylizeValue` is accurate.
|
|
155
209
|
if (shouldExplode(parameter)) {
|
|
156
210
|
return handleExplode(value, parameter);
|
|
157
211
|
}
|
|
@@ -16,8 +16,8 @@ function isURIEncoded(value) {
|
|
|
16
16
|
try {
|
|
17
17
|
return decodeURIComponent(value) !== value;
|
|
18
18
|
} catch (err) {
|
|
19
|
-
// `decodeURIComponent` will throw an exception if a string that has an un-encoded percent sign
|
|
20
|
-
//
|
|
19
|
+
// `decodeURIComponent` will throw an exception if a string that has an un-encoded percent sign
|
|
20
|
+
// in it (like 20%), o if it's throwing we can just assume that the value hasn't been encoded.
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -36,7 +36,7 @@ module.exports = function stylize(config) {
|
|
|
36
36
|
|
|
37
37
|
module.exports.encodeDisallowedCharacters = function encodeDisallowedCharacters(
|
|
38
38
|
str,
|
|
39
|
-
{ escape, returnIfEncoded = false } = {}, // eslint-disable-line default-param-last
|
|
39
|
+
{ escape, returnIfEncoded = false, isAllowedReserved } = {}, // eslint-disable-line default-param-last
|
|
40
40
|
parse
|
|
41
41
|
) {
|
|
42
42
|
if (typeof str === 'number') {
|
|
@@ -61,17 +61,16 @@ module.exports.encodeDisallowedCharacters = function encodeDisallowedCharacters(
|
|
|
61
61
|
return JSON.parse(str);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// In ES6 you can do this quite easily by using the new ... spread operator.
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// code points rather than UCS-2/UTF-16 code units.
|
|
64
|
+
// In ES6 you can do this quite easily by using the new ... spread operator. This causes the
|
|
65
|
+
// string iterator (another new ES6 feature) to be used internally, and because that iterator is
|
|
66
|
+
// designed to deal with code points rather than UCS-2/UTF-16 code units.
|
|
68
67
|
return [...str]
|
|
69
68
|
.map(char => {
|
|
70
69
|
if (isRfc3986Unreserved(char)) {
|
|
71
70
|
return char;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
if (isRfc3986Reserved(char) && escape === 'unsafe') {
|
|
73
|
+
if (isRfc3986Reserved(char) && (escape === 'unsafe' || isAllowedReserved)) {
|
|
75
74
|
return char;
|
|
76
75
|
}
|
|
77
76
|
|
|
@@ -86,142 +85,255 @@ module.exports.encodeDisallowedCharacters = function encodeDisallowedCharacters(
|
|
|
86
85
|
.join('');
|
|
87
86
|
};
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
/**
|
|
89
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-examples}
|
|
90
|
+
*/
|
|
91
|
+
function encodeArray({ location, key, value, style, explode, escape, isAllowedReserved = false }) {
|
|
90
92
|
const valueEncoder = str =>
|
|
91
93
|
module.exports.encodeDisallowedCharacters(str, {
|
|
92
94
|
escape,
|
|
93
95
|
returnIfEncoded: location === 'query',
|
|
96
|
+
isAllowedReserved,
|
|
94
97
|
});
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
99
|
+
switch (style) {
|
|
100
|
+
/**
|
|
101
|
+
* @example <caption>`style: simple`</caption>
|
|
102
|
+
* `["blue","black","brown"]` → `blue,black,brown`
|
|
103
|
+
*/
|
|
104
|
+
case 'simple':
|
|
105
|
+
return value.map(val => valueEncoder(val)).join(',');
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @example <caption>`style: label`</caption>
|
|
109
|
+
* `["blue","black","brown"]` → `.blue.black.brown`
|
|
110
|
+
*/
|
|
111
|
+
case 'label':
|
|
112
|
+
return `.${value.map(val => valueEncoder(val)).join('.')}`;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @example <caption>`style: matrix` + `explode: true`</caption>
|
|
116
|
+
* `["blue","black","brown"]` → `;color=blue;color=black;color=brown`
|
|
117
|
+
*
|
|
118
|
+
* @example <caption>`style: matrix` + `explode: false` (the default behavior)</caption>
|
|
119
|
+
* `["blue","black","brown"]` → `;color=blue,black,brown `
|
|
120
|
+
*/
|
|
121
|
+
case 'matrix':
|
|
122
|
+
return value
|
|
123
|
+
.map(val => valueEncoder(val))
|
|
124
|
+
.reduce((prev, curr) => {
|
|
125
|
+
if (!prev || explode) {
|
|
126
|
+
return `${prev || ''};${key}=${curr}`;
|
|
127
|
+
}
|
|
128
|
+
return `${prev},${curr}`;
|
|
129
|
+
}, '');
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @example <caption>`style: form` + `explode: true`</caption>
|
|
133
|
+
* `["blue","black","brown"]` → `color=blue&color=black&color=brown`
|
|
134
|
+
*
|
|
135
|
+
* @example <caption>`style: form` + `explode: false` (the default behavior)</caption>
|
|
136
|
+
* `["blue","black","brown"]` → `ccolor=blue,black,brown`
|
|
137
|
+
*/
|
|
138
|
+
case 'form':
|
|
139
|
+
return value.map(val => valueEncoder(val)).join(explode ? `&${key}=` : ',');
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @example <caption>`style: spaceDelimited`</caption>
|
|
143
|
+
* `["blue","black","brown"]` → `blue%20black%20brown`
|
|
144
|
+
*/
|
|
145
|
+
case 'spaceDelimited':
|
|
146
|
+
return value.map(val => valueEncoder(val)).join(` ${explode ? `${key}=` : ''}`);
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @example <caption>`style: pipeDelimited`</caption>
|
|
150
|
+
* `["blue","black","brown"]` → `blue|black|brown`
|
|
151
|
+
*/
|
|
152
|
+
case 'pipeDelimited':
|
|
153
|
+
return value.map(val => valueEncoder(val)).join(`|${explode ? `${key}=` : ''}`);
|
|
154
|
+
|
|
155
|
+
default:
|
|
156
|
+
return undefined;
|
|
123
157
|
}
|
|
124
|
-
|
|
125
|
-
if (style === 'pipeDelimited') {
|
|
126
|
-
const after = explode ? `${key}=` : '';
|
|
127
|
-
return value.map(val => valueEncoder(val)).join(`|${after}`);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return undefined;
|
|
131
158
|
}
|
|
132
159
|
|
|
133
|
-
|
|
160
|
+
/**
|
|
161
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-examples}
|
|
162
|
+
*/
|
|
163
|
+
function encodeObject({ location, key, value, style, explode, escape, isAllowedReserved = false }) {
|
|
134
164
|
const valueEncoder = str =>
|
|
135
165
|
module.exports.encodeDisallowedCharacters(str, {
|
|
136
166
|
escape,
|
|
137
167
|
returnIfEncoded: location === 'query',
|
|
168
|
+
isAllowedReserved,
|
|
138
169
|
});
|
|
139
170
|
|
|
140
171
|
const valueKeys = Object.keys(value);
|
|
141
172
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
173
|
+
switch (style) {
|
|
174
|
+
/**
|
|
175
|
+
* @example <caption>`style: simple` + `explode: true`</caption>
|
|
176
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `R=100,G=200,B=150`
|
|
177
|
+
*
|
|
178
|
+
* @example <caption>`style: simple` + `explode: false` (the default behavior)</caption>
|
|
179
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `R,100,G,200,B,150`
|
|
180
|
+
*/
|
|
181
|
+
case 'simple':
|
|
182
|
+
return valueKeys.reduce((prev, curr) => {
|
|
183
|
+
const val = valueEncoder(value[curr]);
|
|
184
|
+
const middleChar = explode ? '=' : ',';
|
|
185
|
+
const prefix = prev ? `${prev},` : '';
|
|
186
|
+
|
|
187
|
+
return `${prefix}${curr}${middleChar}${val}`;
|
|
188
|
+
}, '');
|
|
147
189
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
/**
|
|
191
|
+
* @example <caption>`style: label` + `explode: true`</caption>
|
|
192
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `.R=100.G=200.B=150`
|
|
193
|
+
*
|
|
194
|
+
* @example <caption>`style: label` + `explode: false` (the default behavior)</caption>
|
|
195
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `.R.100.G.200.B.150`
|
|
196
|
+
*/
|
|
197
|
+
case 'label':
|
|
198
|
+
return valueKeys.reduce((prev, curr) => {
|
|
199
|
+
const val = valueEncoder(value[curr]);
|
|
200
|
+
const middleChar = explode ? '=' : '.';
|
|
201
|
+
const prefix = prev ? `${prev}.` : '.';
|
|
202
|
+
|
|
203
|
+
return `${prefix}${curr}${middleChar}${val}`;
|
|
204
|
+
}, '');
|
|
151
205
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
206
|
+
/**
|
|
207
|
+
* @example <caption>`style: matrix` + `explode: true`</caption>
|
|
208
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `;R=100;G=200;B=150`
|
|
209
|
+
*
|
|
210
|
+
* @example <caption>`style: matrix` + `explode: false` (the default behavior)</caption>
|
|
211
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `;color=R,100,G,200,B,150`
|
|
212
|
+
*/
|
|
213
|
+
case 'matrix':
|
|
214
|
+
if (explode) {
|
|
215
|
+
return valueKeys.reduce((prev, curr) => {
|
|
216
|
+
const val = valueEncoder(value[curr]);
|
|
217
|
+
const prefix = prev ? `${prev};` : ';';
|
|
218
|
+
|
|
219
|
+
return `${prefix}${curr}=${val}`;
|
|
220
|
+
}, '');
|
|
221
|
+
}
|
|
157
222
|
|
|
158
|
-
return
|
|
159
|
-
|
|
160
|
-
|
|
223
|
+
return valueKeys.reduce((prev, curr) => {
|
|
224
|
+
const val = valueEncoder(value[curr]);
|
|
225
|
+
const prefix = prev ? `${prev},` : `;${key}=`;
|
|
161
226
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const val = valueEncoder(value[curr]);
|
|
165
|
-
const prefix = prev ? `${prev};` : ';';
|
|
227
|
+
return `${prefix}${curr},${val}`;
|
|
228
|
+
}, '');
|
|
166
229
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
230
|
+
/**
|
|
231
|
+
* @example <caption>`style: form` + `explode: true`</caption>
|
|
232
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `R=100&G=200&B=150`
|
|
233
|
+
*
|
|
234
|
+
* @example <caption>`style: form` + `explode: false` (the default behavior)</caption>
|
|
235
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `color=R,100,G,200,B,150`
|
|
236
|
+
*/
|
|
237
|
+
case 'form':
|
|
238
|
+
return valueKeys.reduce((prev, curr) => {
|
|
239
|
+
const val = valueEncoder(value[curr]);
|
|
240
|
+
const prefix = prev ? `${prev}${explode ? '&' : ','}` : '';
|
|
241
|
+
const separator = explode ? '=' : ',';
|
|
242
|
+
|
|
243
|
+
return `${prefix}${curr}${separator}${val}`;
|
|
244
|
+
}, '');
|
|
170
245
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
246
|
+
/**
|
|
247
|
+
* @example <caption>`style: spaceDelimited`</caption>
|
|
248
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `R%20100%20G%20200%20B%20150`
|
|
249
|
+
*/
|
|
250
|
+
case 'spaceDelimited':
|
|
251
|
+
return valueKeys.reduce((prev, curr) => {
|
|
252
|
+
const val = valueEncoder(value[curr]);
|
|
253
|
+
const prefix = prev ? `${prev} ` : '';
|
|
176
254
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
255
|
+
return `${prefix}${curr} ${val}`;
|
|
256
|
+
}, '');
|
|
180
257
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
258
|
+
/**
|
|
259
|
+
* @example <caption>`style: pipeDelimited`</caption>
|
|
260
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `R|100|G|200|B|150`
|
|
261
|
+
*/
|
|
262
|
+
case 'pipeDelimited':
|
|
263
|
+
return valueKeys.reduce((prev, curr) => {
|
|
264
|
+
const val = valueEncoder(value[curr]);
|
|
265
|
+
const prefix = prev ? `${prev}|` : '';
|
|
186
266
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
267
|
+
return `${prefix}${curr}|${val}`;
|
|
268
|
+
}, '');
|
|
190
269
|
|
|
191
|
-
|
|
270
|
+
/**
|
|
271
|
+
* @example <caption>`style: deepObject`</caption>
|
|
272
|
+
* `{ "R": 100, "G": 200, "B": 150 }` → `color[R]=100&color[G]=200&color[B]=150`
|
|
273
|
+
*/
|
|
274
|
+
case 'deepObject':
|
|
275
|
+
return valueKeys.reduce(curr => {
|
|
276
|
+
const val = valueEncoder(value[curr], {}, true);
|
|
277
|
+
return `${val}`;
|
|
278
|
+
}, '');
|
|
279
|
+
|
|
280
|
+
default:
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
192
283
|
}
|
|
193
284
|
|
|
194
|
-
|
|
285
|
+
/**
|
|
286
|
+
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-examples}
|
|
287
|
+
*/
|
|
288
|
+
function encodePrimitive({ location, key, value, style, escape, isAllowedReserved = false }) {
|
|
195
289
|
const valueEncoder = str =>
|
|
196
290
|
module.exports.encodeDisallowedCharacters(str, {
|
|
197
291
|
escape,
|
|
198
292
|
returnIfEncoded: location === 'query' || location === 'body',
|
|
293
|
+
isAllowedReserved,
|
|
199
294
|
});
|
|
200
295
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
296
|
+
switch (style) {
|
|
297
|
+
/**
|
|
298
|
+
* @example <caption>`style: simple`</caption>
|
|
299
|
+
* `blue` → `blue`
|
|
300
|
+
*/
|
|
301
|
+
case 'simple':
|
|
302
|
+
return valueEncoder(value);
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* @example <caption>`style: label`</caption>
|
|
306
|
+
* `blue` → `.blue`
|
|
307
|
+
*/
|
|
308
|
+
case 'label':
|
|
309
|
+
return `.${valueEncoder(value)}`;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @example <caption>`style: matrix`</caption>
|
|
313
|
+
* `blue` → `;color=blue`
|
|
314
|
+
*/
|
|
315
|
+
case 'matrix':
|
|
316
|
+
if (value === '') {
|
|
317
|
+
return `;${key}`;
|
|
318
|
+
}
|
|
208
319
|
|
|
209
|
-
|
|
210
|
-
// This conditional added by Aaron to be more accurate to the spec
|
|
211
|
-
if (value === '') {
|
|
212
|
-
return `;${key}`;
|
|
213
|
-
}
|
|
320
|
+
return `;${key}=${valueEncoder(value)}`;
|
|
214
321
|
|
|
215
|
-
|
|
216
|
-
|
|
322
|
+
/**
|
|
323
|
+
* @example <caption>`style: form`</caption>
|
|
324
|
+
* `blue` → `color=blue`
|
|
325
|
+
*/
|
|
326
|
+
case 'form':
|
|
327
|
+
return valueEncoder(value);
|
|
217
328
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
329
|
+
/**
|
|
330
|
+
* @example <caption>`style: deepObject`</caption>
|
|
331
|
+
* `blue` → n/a
|
|
332
|
+
*/
|
|
333
|
+
case 'deepObject':
|
|
334
|
+
return valueEncoder(value, {}, true);
|
|
221
335
|
|
|
222
|
-
|
|
223
|
-
|
|
336
|
+
default:
|
|
337
|
+
return undefined;
|
|
224
338
|
}
|
|
225
|
-
|
|
226
|
-
return undefined;
|
|
227
339
|
}
|
package/.github/dependabot.yml
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
updates:
|
|
3
|
-
- package-ecosystem: github-actions
|
|
4
|
-
directory: "/"
|
|
5
|
-
schedule:
|
|
6
|
-
interval: monthly
|
|
7
|
-
reviewers:
|
|
8
|
-
- erunion
|
|
9
|
-
labels:
|
|
10
|
-
- dependencies
|
|
11
|
-
commit-message:
|
|
12
|
-
prefix: chore(deps)
|
|
13
|
-
prefix-development: chore(deps-dev)
|
|
14
|
-
|
|
15
|
-
- package-ecosystem: npm
|
|
16
|
-
directory: "/"
|
|
17
|
-
schedule:
|
|
18
|
-
interval: monthly
|
|
19
|
-
open-pull-requests-limit: 10
|
|
20
|
-
reviewers:
|
|
21
|
-
- erunion
|
|
22
|
-
labels:
|
|
23
|
-
- dependencies
|
|
24
|
-
commit-message:
|
|
25
|
-
prefix: chore(deps)
|
|
26
|
-
prefix-development: chore(deps-dev)
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on: [push]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
build:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
strategy:
|
|
9
|
-
matrix:
|
|
10
|
-
node-version: [12.x, 14.x, 16.x]
|
|
11
|
-
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v2.4.0
|
|
14
|
-
|
|
15
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
16
|
-
uses: actions/setup-node@v2.5.1
|
|
17
|
-
with:
|
|
18
|
-
node-version: ${{ matrix.node-version }}
|
|
19
|
-
|
|
20
|
-
- name: Install deps
|
|
21
|
-
run: npm ci
|
|
22
|
-
|
|
23
|
-
- name: Run tests
|
|
24
|
-
run: npm test
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
name: "CodeQL"
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ main ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ main ]
|
|
8
|
-
schedule:
|
|
9
|
-
- cron: '0 0 1 * *'
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
analyze:
|
|
13
|
-
name: Analyze
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
permissions:
|
|
16
|
-
actions: read
|
|
17
|
-
contents: read
|
|
18
|
-
security-events: write
|
|
19
|
-
|
|
20
|
-
strategy:
|
|
21
|
-
fail-fast: false
|
|
22
|
-
matrix:
|
|
23
|
-
language: [ 'javascript' ]
|
|
24
|
-
|
|
25
|
-
steps:
|
|
26
|
-
- name: Checkout repository
|
|
27
|
-
uses: actions/checkout@v2.4.0
|
|
28
|
-
|
|
29
|
-
- name: Initialize CodeQL
|
|
30
|
-
uses: github/codeql-action/init@v1
|
|
31
|
-
with:
|
|
32
|
-
languages: ${{ matrix.language }}
|
|
33
|
-
|
|
34
|
-
- name: Perform CodeQL Analysis
|
|
35
|
-
uses: github/codeql-action/analyze@v1
|
package/.husky/commit-msg
DELETED
package/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
-
nationality, personal appearance, race, religion, or sexual identity
|
|
10
|
-
and orientation.
|
|
11
|
-
|
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
-
diverse, inclusive, and healthy community.
|
|
14
|
-
|
|
15
|
-
## Our Standards
|
|
16
|
-
|
|
17
|
-
Examples of behavior that contributes to a positive environment for our
|
|
18
|
-
community include:
|
|
19
|
-
|
|
20
|
-
* Demonstrating empathy and kindness toward other people
|
|
21
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
-
* Giving and gracefully accepting constructive feedback
|
|
23
|
-
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
-
and learning from the experience
|
|
25
|
-
* Focusing on what is best not just for us as individuals, but for the
|
|
26
|
-
overall community
|
|
27
|
-
|
|
28
|
-
Examples of unacceptable behavior include:
|
|
29
|
-
|
|
30
|
-
* The use of sexualized language or imagery, and sexual attention or
|
|
31
|
-
advances of any kind
|
|
32
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
-
* Public or private harassment
|
|
34
|
-
* Publishing others' private information, such as a physical or email
|
|
35
|
-
address, without their explicit permission
|
|
36
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
-
professional setting
|
|
38
|
-
|
|
39
|
-
## Enforcement Responsibilities
|
|
40
|
-
|
|
41
|
-
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
-
or harmful.
|
|
45
|
-
|
|
46
|
-
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
-
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
-
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
-
decisions when appropriate.
|
|
50
|
-
|
|
51
|
-
## Scope
|
|
52
|
-
|
|
53
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
-
an individual is officially representing the community in public spaces.
|
|
55
|
-
Examples of representing our community include using an official e-mail address,
|
|
56
|
-
posting via an official social media account, or acting as an appointed
|
|
57
|
-
representative at an online or offline event.
|
|
58
|
-
|
|
59
|
-
## Enforcement
|
|
60
|
-
|
|
61
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
-
reported to the community leaders responsible for enforcement at
|
|
63
|
-
[support+coc@readme.io](mailto:support+coc@readme.io).
|
|
64
|
-
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
-
|
|
66
|
-
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
-
reporter of any incident.
|
|
68
|
-
|
|
69
|
-
## Enforcement Guidelines
|
|
70
|
-
|
|
71
|
-
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
-
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
-
|
|
74
|
-
### 1. Correction
|
|
75
|
-
|
|
76
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
-
unprofessional or unwelcome in the community.
|
|
78
|
-
|
|
79
|
-
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
-
clarity around the nature of the violation and an explanation of why the
|
|
81
|
-
behavior was inappropriate. A public apology may be requested.
|
|
82
|
-
|
|
83
|
-
### 2. Warning
|
|
84
|
-
|
|
85
|
-
**Community Impact**: A violation through a single incident or series
|
|
86
|
-
of actions.
|
|
87
|
-
|
|
88
|
-
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
-
interaction with the people involved, including unsolicited interaction with
|
|
90
|
-
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
-
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
-
like social media. Violating these terms may lead to a temporary or
|
|
93
|
-
permanent ban.
|
|
94
|
-
|
|
95
|
-
### 3. Temporary Ban
|
|
96
|
-
|
|
97
|
-
**Community Impact**: A serious violation of community standards, including
|
|
98
|
-
sustained inappropriate behavior.
|
|
99
|
-
|
|
100
|
-
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
-
communication with the community for a specified period of time. No public or
|
|
102
|
-
private interaction with the people involved, including unsolicited interaction
|
|
103
|
-
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
-
Violating these terms may lead to a permanent ban.
|
|
105
|
-
|
|
106
|
-
### 4. Permanent Ban
|
|
107
|
-
|
|
108
|
-
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
-
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
-
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
-
|
|
112
|
-
**Consequence**: A permanent ban from any sort of public interaction within
|
|
113
|
-
the community.
|
|
114
|
-
|
|
115
|
-
## Attribution
|
|
116
|
-
|
|
117
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
-
version 2.0, available at
|
|
119
|
-
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
|
120
|
-
|
|
121
|
-
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
|
122
|
-
enforcement ladder](https://github.com/mozilla/diversity).
|
|
123
|
-
|
|
124
|
-
[homepage]: https://www.contributor-covenant.org
|
|
125
|
-
|
|
126
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
|
127
|
-
https://www.contributor-covenant.org/faq. Translations are available at
|
|
128
|
-
https://www.contributor-covenant.org/translations.
|
package/SECURITY.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Reporting a Vulnerability
|
|
4
|
-
|
|
5
|
-
If there are any vulnerabilities in `@readme/oas-to-har`, don't hesitate to _report them_.
|
|
6
|
-
|
|
7
|
-
Please email security@readme.io and describe what you've found.
|
|
8
|
-
|
|
9
|
-
- If you have a fix, explain or attach it.
|
|
10
|
-
- In the near time, expect a reply with the required steps. Also, there may be a demand for a pull request which include the fixes.
|
|
11
|
-
|
|
12
|
-
> You should not disclose the vulnerability publicly if you haven't received an answer in some weeks. If the vulnerability is rejected, you may post it publicly within some hour of rejection, unless the rejection is withdrawn within that time period. After the vulnerability has been fixed, you may disclose the vulnerability details publicly over some days.
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
function isEmptyObject(obj) {
|
|
2
|
-
// Then remove all empty objects from the top level object
|
|
3
|
-
return typeof obj === 'object' && Object.keys(obj).length === 0;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
// Modified from here: https://stackoverflow.com/a/43781499
|
|
7
|
-
function stripEmptyObjects(obj) {
|
|
8
|
-
let cleanObj = obj;
|
|
9
|
-
|
|
10
|
-
Object.keys(cleanObj).forEach(key => {
|
|
11
|
-
let value = cleanObj[key];
|
|
12
|
-
|
|
13
|
-
if (typeof value === 'object' && !Array.isArray(cleanObj) && value !== null) {
|
|
14
|
-
// Recurse, strip out empty objects from children
|
|
15
|
-
value = stripEmptyObjects(value);
|
|
16
|
-
|
|
17
|
-
// Then remove all empty objects from the top level object
|
|
18
|
-
if (isEmptyObject(value)) {
|
|
19
|
-
delete cleanObj[key];
|
|
20
|
-
} else {
|
|
21
|
-
cleanObj[key] = value;
|
|
22
|
-
}
|
|
23
|
-
} else if (value === null) {
|
|
24
|
-
delete cleanObj[key];
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (Array.isArray(cleanObj)) {
|
|
29
|
-
// Since deleting a key from an array will retain an undefined value in that array, we need to
|
|
30
|
-
// filter them out.
|
|
31
|
-
cleanObj = cleanObj.filter(function (el) {
|
|
32
|
-
return el !== undefined;
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return cleanObj;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function removeUndefinedObjects(obj) {
|
|
40
|
-
// JSON.stringify removes undefined values. Though `[undefined]` will be converted with this to
|
|
41
|
-
// `[null]`, we'll clean that up next.
|
|
42
|
-
let withoutUndefined = JSON.parse(JSON.stringify(obj));
|
|
43
|
-
|
|
44
|
-
// Then we recursively remove all empty objects and nullish arrays.
|
|
45
|
-
withoutUndefined = stripEmptyObjects(withoutUndefined);
|
|
46
|
-
|
|
47
|
-
// If the only thing that's leftover is an empty object
|
|
48
|
-
// then return nothing so we don't end up with default
|
|
49
|
-
// code samples with:
|
|
50
|
-
// --data '{}'
|
|
51
|
-
if (isEmptyObject(withoutUndefined)) return undefined;
|
|
52
|
-
|
|
53
|
-
return withoutUndefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
module.exports = removeUndefinedObjects;
|