@readme/oas-to-har 14.0.3 → 15.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.
@@ -13,7 +13,7 @@ jobs:
13
13
  - uses: actions/checkout@v2.4.0
14
14
 
15
15
  - name: Use Node.js ${{ matrix.node-version }}
16
- uses: actions/setup-node@v2.5.0
16
+ uses: actions/setup-node@v2.5.1
17
17
  with:
18
18
  node-version: ${{ matrix.node-version }}
19
19
 
package/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ ## 15.0.0 (2022-02-03)
2
+
3
+ > **BREAKING RELEASE**
4
+ >
5
+ > `oas-to-har` now assumes that the OpenAPI definition you're supplying it has been fully dereferenced.
6
+
7
+ * feat: upgrading oas and replacing some deprecated accessors (#58) ([ed97a5d](https://github.com/readmeio/oas-to-har/commit/ed97a5d)), closes [#58](https://github.com/readmeio/oas-to-har/issues/58)
8
+ * chore(deps-dev): bump @readme/eslint-config from 8.1.2 to 8.2.0 (#54) ([f783873](https://github.com/readmeio/oas-to-har/commit/f783873)), closes [#54](https://github.com/readmeio/oas-to-har/issues/54)
9
+ * chore(deps-dev): bump @readme/oas-examples from 4.3.3 to 4.4.0 (#53) ([fec1f82](https://github.com/readmeio/oas-to-har/commit/fec1f82)), closes [#53](https://github.com/readmeio/oas-to-har/issues/53)
10
+ * chore(deps-dev): bump eslint from 8.7.0 to 8.8.0 (#56) ([0559975](https://github.com/readmeio/oas-to-har/commit/0559975)), closes [#56](https://github.com/readmeio/oas-to-har/issues/56)
11
+ * chore(deps-dev): bump jest-expect-har from 3.0.1 to 3.0.2 (#57) ([cd2c19e](https://github.com/readmeio/oas-to-har/commit/cd2c19e)), closes [#57](https://github.com/readmeio/oas-to-har/issues/57)
12
+
13
+
14
+
15
+ ## 14.1.0 (2022-01-25)
16
+
17
+ * chore(deps): bumping all out of date deps (#51) ([926763b](https://github.com/readmeio/oas-to-har/commit/926763b)), closes [#51](https://github.com/readmeio/oas-to-har/issues/51)
18
+ * chore(deps): bumping node-fetch ([1405d30](https://github.com/readmeio/oas-to-har/commit/1405d30))
19
+ * feat: add support for styles on multipart/form-data request bodies (#50) ([e7596bd](https://github.com/readmeio/oas-to-har/commit/e7596bd)), closes [#50](https://github.com/readmeio/oas-to-har/issues/50)
20
+ * fix: check for orphaned apiDefinition-less pages (#52) ([04ed408](https://github.com/readmeio/oas-to-har/commit/04ed408)), closes [#52](https://github.com/readmeio/oas-to-har/issues/52)
21
+
22
+
23
+
24
+ ## <small>14.0.5 (2022-01-03)</small>
25
+
26
+ * chore(deps-dev): bump @commitlint/cli from 15.0.0 to 16.0.1 (#45) ([4ce0b94](https://github.com/readmeio/oas-to-har/commit/4ce0b94)), closes [#45](https://github.com/readmeio/oas-to-har/issues/45)
27
+ * chore(deps-dev): bump @commitlint/config-conventional (#48) ([dc5195a](https://github.com/readmeio/oas-to-har/commit/dc5195a)), closes [#48](https://github.com/readmeio/oas-to-har/issues/48)
28
+ * chore(deps-dev): bump @readme/eslint-config from 8.0.4 to 8.1.1 (#46) ([8788c1e](https://github.com/readmeio/oas-to-har/commit/8788c1e)), closes [#46](https://github.com/readmeio/oas-to-har/issues/46)
29
+ * chore(deps-dev): bump eslint from 8.4.1 to 8.6.0 (#47) ([e211feb](https://github.com/readmeio/oas-to-har/commit/e211feb)), closes [#47](https://github.com/readmeio/oas-to-har/issues/47)
30
+ * chore(deps): bump actions/setup-node from 2.5.0 to 2.5.1 (#43) ([a05def3](https://github.com/readmeio/oas-to-har/commit/a05def3)), closes [#43](https://github.com/readmeio/oas-to-har/issues/43)
31
+ * chore(deps): bump oas from 17.3.2 to 17.4.0 (#44) ([8abb825](https://github.com/readmeio/oas-to-har/commit/8abb825)), closes [#44](https://github.com/readmeio/oas-to-har/issues/44)
32
+ * chore(deps): upgrading oas and @readme/oas-extensions (#49) ([822cb6d](https://github.com/readmeio/oas-to-har/commit/822cb6d)), closes [#49](https://github.com/readmeio/oas-to-har/issues/49)
33
+
34
+
35
+
36
+ ## <small>14.0.4 (2021-12-16)</small>
37
+
38
+ * fix: issue where we'd try to stylize iterate over a nullilsh value (#42) ([5baa462](https://github.com/readmeio/oas-to-har/commit/5baa462)), closes [#42](https://github.com/readmeio/oas-to-har/issues/42)
39
+
40
+
41
+
1
42
  ## <small>14.0.3 (2021-12-16)</small>
2
43
 
3
44
  * fix: cleaning up engine requirements and updating dev deps (#40) ([c377906](https://github.com/readmeio/oas-to-har/commit/c377906)), closes [#40](https://github.com/readmeio/oas-to-har/issues/40)
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": "14.0.3",
4
+ "version": "15.0.0",
5
5
  "main": "src/index.js",
6
6
  "author": "Jon Ursenbach <jon@ursenba.ch>",
7
7
  "license": "ISC",
@@ -16,24 +16,24 @@
16
16
  "lint": "eslint .",
17
17
  "prepare": "husky install",
18
18
  "pretest": "npm run lint",
19
- "prettier": "prettier --list-different --write \"./**/**.{js,jsx}\"",
19
+ "prettier": "prettier --list-different --write \"./**/**.{js}\"",
20
20
  "release": "npx conventional-changelog-cli -i CHANGELOG.md -s && git add CHANGELOG.md",
21
21
  "test": "jest --coverage"
22
22
  },
23
23
  "dependencies": {
24
- "@readme/oas-extensions": "^14.0.3",
25
- "oas": "^17.3.2",
24
+ "@readme/oas-extensions": "^14.1.1",
25
+ "oas": "^17.7.1",
26
26
  "parse-data-url": "^4.0.1"
27
27
  },
28
28
  "devDependencies": {
29
- "@commitlint/cli": "^15.0.0",
30
- "@commitlint/config-conventional": "^15.0.0",
31
- "@readme/eslint-config": "^8.0.4",
32
- "@readme/oas-examples": "^4.3.2",
29
+ "@commitlint/cli": "^16.1.0",
30
+ "@commitlint/config-conventional": "^16.0.0",
31
+ "@readme/eslint-config": "^8.1.2",
32
+ "@readme/oas-examples": "^4.3.3",
33
33
  "datauri": "^4.1.0",
34
- "eslint": "^8.4.1",
34
+ "eslint": "^8.7.0",
35
35
  "husky": "^7.0.4",
36
- "jest": "^27.4.5",
36
+ "jest": "^27.4.7",
37
37
  "jest-expect-har": "^3.0.1",
38
38
  "prettier": "^2.5.1"
39
39
  },
package/src/index.js CHANGED
@@ -6,7 +6,7 @@ const configureSecurity = require('./lib/configure-security');
6
6
  const removeUndefinedObjects = require('./lib/remove-undefined-objects');
7
7
  const formatStyle = require('./lib/style-formatting');
8
8
 
9
- const { findSchemaDefinition, getSchema, jsonSchemaTypes } = utils;
9
+ const { jsonSchemaTypes } = utils;
10
10
 
11
11
  function formatter(values, param, type, onlyIfExists) {
12
12
  if (param.style) {
@@ -18,6 +18,7 @@ function formatter(values, param, type, onlyIfExists) {
18
18
 
19
19
  let value;
20
20
 
21
+ // Handle missing values
21
22
  if (typeof values[type][param.name] !== 'undefined') {
22
23
  value = values[type][param.name];
23
24
  } else if (onlyIfExists && !param.required) {
@@ -30,6 +31,11 @@ function formatter(values, param, type, onlyIfExists) {
30
31
  return param.name;
31
32
  }
32
33
 
34
+ // Handle file uploads. Specifically arrays of file uploads which need to be formatted very specifically.
35
+ if (param.schema && param.schema.type === 'array' && param.schema.items && param.schema.items.format === 'binary') {
36
+ return JSON.stringify(value);
37
+ }
38
+
33
39
  if (value !== undefined) {
34
40
  // Query params should always be formatted, even if they don't have a `style` serialization configured.
35
41
  if (type === 'query') {
@@ -42,6 +48,39 @@ function formatter(values, param, type, onlyIfExists) {
42
48
  return undefined;
43
49
  }
44
50
 
51
+ function multipartBodyToFormatterParams(multipartBody, oasMediaTypeObject) {
52
+ const schema = oasMediaTypeObject.schema;
53
+ const encoding = oasMediaTypeObject.encoding;
54
+
55
+ if (typeof multipartBody === 'object' && multipartBody !== null) {
56
+ return Object.keys(multipartBody)
57
+ .map(key => {
58
+ // If we have an incoming parameter, but it's not in the schema
59
+ // ignore it
60
+ if (!schema.properties[key]) {
61
+ return false;
62
+ }
63
+
64
+ const paramEncoding = encoding ? encoding[key] : undefined;
65
+
66
+ return {
67
+ name: key,
68
+ // If the style isn't defined, use the default
69
+ style: paramEncoding ? paramEncoding.style : undefined,
70
+ // If explode isn't defined, use the default
71
+ explode: paramEncoding ? paramEncoding.explode : undefined,
72
+ required: schema.required && schema.required.includes(key),
73
+ schema: schema.properties[key],
74
+ in: 'body',
75
+ };
76
+ })
77
+ .filter(Boolean);
78
+ }
79
+
80
+ // Pretty sure that we'll never have anything but an object for multipart bodies, so returning empty array if we get anything else.
81
+ return [];
82
+ }
83
+
45
84
  const defaultFormDataTypes = Object.keys(jsonSchemaTypes).reduce((prev, curr) => {
46
85
  return Object.assign(prev, { [curr]: {} });
47
86
  }, {});
@@ -75,7 +114,7 @@ function stringifyParameter(param) {
75
114
  return JSON.stringify(param);
76
115
  }
77
116
 
78
- function appendHarValue(harParam, name, value) {
117
+ function appendHarValue(harParam, name, value, addtlData = {}) {
79
118
  if (typeof value === 'undefined') return;
80
119
 
81
120
  if (Array.isArray(value)) {
@@ -91,6 +130,7 @@ function appendHarValue(harParam, name, value) {
91
130
  } else {
92
131
  // If the formatter gives us a non-array, non-object, we add it as is
93
132
  harParam.push({
133
+ ...addtlData,
94
134
  name,
95
135
  value: String(value),
96
136
  });
@@ -148,22 +188,7 @@ module.exports = (
148
188
  }
149
189
  }
150
190
 
151
- // Does this operation have any parameters?
152
- const parameters = [];
153
- function addParameter(param) {
154
- if (param.$ref) {
155
- parameters.push(findSchemaDefinition(param.$ref, apiDefinition));
156
- } else {
157
- parameters.push(param);
158
- }
159
- }
160
-
161
- operation.getParameters().forEach(addParameter);
162
-
163
- // Does this operation have any common parameters?
164
- if (apiDefinition.paths && apiDefinition.paths[operation.path] && apiDefinition.paths[operation.path].parameters) {
165
- apiDefinition.paths[operation.path].parameters.forEach(addParameter);
166
- }
191
+ const parameters = operation.getParameters();
167
192
 
168
193
  har.url = har.url.replace(/{([-_a-zA-Z0-9[\]]+)}/g, (full, key) => {
169
194
  if (!operation || !parameters) return key; // No path params at all
@@ -254,14 +279,12 @@ module.exports = (
254
279
  });
255
280
  }
256
281
 
257
- let requestBody = getSchema(operation.schema, apiDefinition);
258
- if (requestBody) {
259
- requestBody = requestBody.schema;
260
- } else {
261
- requestBody = { schema: {} };
282
+ let requestBody = false;
283
+ if (operation.hasRequestBody()) {
284
+ [, requestBody] = operation.getRequestBody();
262
285
  }
263
286
 
264
- if (requestBody.schema && Object.keys(requestBody.schema).length) {
287
+ if (requestBody && requestBody.schema && Object.keys(requestBody.schema).length) {
265
288
  if (operation.isFormUrlEncoded()) {
266
289
  if (Object.keys(formData.formData).length) {
267
290
  const cleanFormData = removeUndefinedObjects(JSON.parse(JSON.stringify(formData.formData)));
@@ -300,39 +323,32 @@ module.exports = (
300
323
  );
301
324
 
302
325
  if (cleanBody !== undefined) {
303
- Object.keys(cleanBody).forEach(name => {
304
- // We neither have an easy way to transform `name` into `name[]` to signify that it's an array payload
305
- // (and for all we know it might be an array of objects!), but also at the same time the HAR spec
306
- // doesn't give any guidance for these kinds of cases so instead we're just falling back to
307
- // stringifying the content instead of potentially including `fileName` properties.
308
- if (Array.isArray(cleanBody[name])) {
309
- har.postData.params.push({
310
- name,
311
- value: JSON.stringify(cleanBody[name]),
312
- });
326
+ const multipartParams = multipartBodyToFormatterParams(
327
+ formData.body,
328
+ operation.schema.requestBody.content['multipart/form-data']
329
+ );
313
330
 
314
- return;
315
- }
331
+ Object.keys(cleanBody).forEach(name => {
332
+ const param = multipartParams.find(multipartParam => multipartParam.name === name);
316
333
 
317
- const data = {
318
- name,
319
- value: String(cleanBody[name]),
320
- };
334
+ const value = formatter(formData, param, 'body', true);
321
335
 
322
336
  // If we're dealing with a binary type, and the value is a valid data URL we should parse out any
323
337
  // available filename and content type to send along with the parameter to interpreters like `fetch-har`
324
338
  // can make sense of it and send a usable payload.
339
+ const addtlData = {};
340
+
325
341
  if (binaryTypes.includes(name)) {
326
- const parsed = parseDataUrl(data.value);
342
+ const parsed = parseDataUrl(value);
327
343
  if (parsed) {
328
- data.fileName = 'name' in parsed ? parsed.name : 'unknown';
344
+ addtlData.fileName = 'name' in parsed ? parsed.name : 'unknown';
329
345
  if ('contentType' in parsed) {
330
- data.contentType = parsed.contentType;
346
+ addtlData.contentType = parsed.contentType;
331
347
  }
332
348
  }
333
349
  }
334
350
 
335
- har.postData.params.push(data);
351
+ appendHarValue(har.postData.params, name, value, addtlData);
336
352
  });
337
353
  }
338
354
  } else {
@@ -379,6 +395,8 @@ module.exports = (
379
395
  }
380
396
  }
381
397
  } catch (e) {
398
+ // you should log this error if you're debugging why data is showing up in text, when it should show up in params
399
+ // console.log('catching ', e);
382
400
  // If anything above fails for whatever reason, assume that whatever we had is invalid JSON and just treat it
383
401
  // as raw text.
384
402
  har.postData.text = stringify(formData.body);
@@ -396,7 +414,7 @@ module.exports = (
396
414
 
397
415
  // Add a `Content-Type` header if there are any body values setup above or if there is a schema defined, but only do
398
416
  // so if we don't already have a `Content-Type` present as it's impossible for a request to have multiple.
399
- if ((har.postData.text || Object.keys(requestBody.schema).length) && !hasContentType) {
417
+ if ((har.postData.text || (requestBody && Object.keys(requestBody.schema).length)) && !hasContentType) {
400
418
  har.headers.push({
401
419
  name: 'Content-Type',
402
420
  value: contentType,
@@ -114,7 +114,7 @@ function handleExplode(value, parameter) {
114
114
  });
115
115
  }
116
116
 
117
- if (typeof value === 'object') {
117
+ if (typeof value === 'object' && value !== null) {
118
118
  const newObj = {};
119
119
 
120
120
  Object.keys(value).forEach(key => {
@@ -195,7 +195,7 @@ function encodePrimitive({ location, key, value, style, escape }) {
195
195
  const valueEncoder = str =>
196
196
  module.exports.encodeDisallowedCharacters(str, {
197
197
  escape,
198
- returnIfEncoded: location === 'query',
198
+ returnIfEncoded: location === 'query' || location === 'body',
199
199
  });
200
200
 
201
201
  if (style === 'simple') {