From 998cbf569a72ea4687c0d6306bc7eb4b06e2dcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Wed, 16 Mar 2022 21:43:15 +0100 Subject: [PATCH 01/16] build: release from beta --- .circleci/config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bc39f3e0..b3eea75f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,7 +37,9 @@ workflows: - release: filters: branches: - only: master + only: + - beta + - master requires: - test - build From f6155514f5521fbedd5e8497edc5749eb2e885f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Wed, 23 Mar 2022 21:24:05 +0100 Subject: [PATCH 02/16] feat: drop urijs (#167) --- package.json | 6 +- src/__tests__/utils.test.ts | 120 +------------ src/index.ts | 4 + src/oas2/guards.ts | 2 +- src/oas2/transformers/servers.ts | 14 +- src/postman/transformers/server.ts | 9 +- src/utils.ts | 48 +---- yarn.lock | 278 +++++++++++------------------ 8 files changed, 129 insertions(+), 352 deletions(-) create mode 100644 src/index.ts diff --git a/package.json b/package.json index a8a7b393..9b72a915 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,9 @@ "test": "jest" }, "dependencies": { - "@stoplight/json": "^3.10.2", + "@stoplight/json": "^3.17.1", "@stoplight/types": "^12.4.0", "@types/swagger-schema-official": "~2.0.21", - "@types/to-json-schema": "^0.2.0", "@types/type-is": "^1.6.3", "@types/urijs": "~1.19.9", "json-schema": "^0.4.0", @@ -39,8 +38,7 @@ "lodash": "^4.17.15", "openapi3-ts": "^2.0.1", "postman-collection": "^4.1.0", - "type-is": "^1.6.18", - "urijs": "~1.19.2" + "type-is": "^1.6.18" }, "devDependencies": { "@stoplight/eslint-config": "^1.1.0", diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 12116421..57cb1795 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,122 +1,4 @@ -import * as urijs from 'urijs'; - -import { maybeResolveLocalRef, URI } from '../utils'; - -describe('URI()', () => { - it('instantiates from string', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).toString()).toEqual(url); - }); - - it('instantiates from urijs', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(urijs(url)).toString()).toEqual(url); - }); - - describe('type()', () => { - it('sets type', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).scheme('test2').toString()).toEqual('test2:///path/to/api.yaml#/the/pointer'); - }); - }); - - describe('host()', () => { - it('sets host', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).host('test2.com').toString()).toEqual('test://test2.com/path/to/api.yaml#/the/pointer'); - }); - - it('sets host and port', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).host('test2:3000').toString()).toEqual('test://test2:3000/path/to/api.yaml#/the/pointer'); - }); - - it('sets host and port (non-number port does not error)', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).host('test2:{$$.env.host}').toString()).toEqual( - 'test://test2:{$$.env.host}/path/to/api.yaml#/the/pointer', - ); - }); - }); - - describe('port()', () => { - it('sets port', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).host('test2').port('3000').toString()).toEqual('test://test2:3000/path/to/api.yaml#/the/pointer'); - }); - - it('does not error on invalid port', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).host('test2').port('invalid').toString()).toEqual('test://test2/path/to/api.yaml#/the/pointer'); - }); - }); - - describe('path()', () => { - it('sets path', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).path('/path/to/other/api.yaml').toString()).toEqual( - 'test:///path/to/other/api.yaml#/the/pointer', - ); - }); - }); - - describe('pointer()', () => { - describe('pointer is a string', () => { - it('sets pointer', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).pointer('#/new/pointer').toString()).toEqual('test:///path/to/api.yaml#/new/pointer'); - }); - }); - - describe('pointer is a path', () => { - it('sets pointer', () => { - const url = 'test:///path/to/api.yaml#/the/pointer'; - - expect(URI(url).pointer(['new', 'pointer']).toString()).toEqual('test:///path/to/api.yaml#/new/pointer'); - }); - }); - }); - - describe('append()', () => { - describe('append pointer to uri having a pointer', () => { - it('replaces pointer', () => { - expect(URI('file:///path/to/api.yaml#/some/pointer').append('#/some/other/pointer').toString()).toEqual( - 'file:///path/to/api.yaml#/some/other/pointer', - ); - }); - }); - - describe('append element to uri having a pointer', () => { - it('appends element to pointer', () => { - expect(URI('file:///path/to/api.yaml#/some/pointer').append('inner').toString()).toEqual( - 'file:///path/to/api.yaml#/some/pointer/inner', - ); - }); - }); - - describe('append pointer to uri not having a pointer', () => { - it('appends pointer to uri', () => { - expect(URI('file:///path/to/api.yaml').append('#/some').toString()).toEqual('file:///path/to/api.yaml#/some'); - }); - }); - - describe('append element to uri not having a pointer', () => { - it('appends element to uri', () => { - expect(URI('file:///path/to').append('api.yaml').toString()).toEqual('file:///path/to/api.yaml'); - }); - }); - }); -}); +import { maybeResolveLocalRef } from '../utils'; describe('maybeResolveLocalRef()', () => { it('follows $refs', () => { diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..cfc451c5 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,4 @@ +export * from './oas'; +export * from './oas2'; +export * from './oas3'; +export * from './postman'; diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index 7f568be2..7708867e 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -22,6 +22,6 @@ export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObj 'headers' in maybeResponseObject || 'examples' in maybeResponseObject); -export function isValidScheme(scheme: unknown) { +export function isValidScheme(scheme: unknown): scheme is 'http' | 'https' | 'ws' | 'wss' { return typeof scheme === 'string' && ['http', 'https', 'ws', 'wss'].includes(scheme); } diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index bca9cc8c..2dee9fd6 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,11 +1,11 @@ import { DeepPartial, IServer } from '@stoplight/types'; import { Operation, Spec } from 'swagger-schema-official'; -import { URI } from '../../utils'; import { isValidScheme } from '../guards'; export function translateToServers(spec: DeepPartial, operation: DeepPartial): IServer[] { - if (typeof spec.host !== 'string' || spec.host.length === 0) { + const { host } = spec; + if (typeof host !== 'string' || host.length === 0) { return []; } @@ -14,13 +14,15 @@ export function translateToServers(spec: DeepPartial, operation: DeepParti return []; } - const hasBasePath = typeof spec.basePath === 'string' && spec.basePath.length > 0; + const basePath = typeof spec.basePath === 'string' && spec.basePath.length > 0 ? spec.basePath : null; return schemes.filter(isValidScheme).map(scheme => { - let uri = URI().scheme(scheme).host(spec.host); + const uri = new URL('https://localhost'); + uri.host = host; + uri.protocol = `${scheme}:`; - if (hasBasePath) { - uri = uri.path(spec.basePath); + if (basePath !== null) { + uri.pathname = basePath; } return { diff --git a/src/postman/transformers/server.ts b/src/postman/transformers/server.ts index 3073e257..e2898163 100644 --- a/src/postman/transformers/server.ts +++ b/src/postman/transformers/server.ts @@ -1,8 +1,11 @@ import { IServer } from '@stoplight/types'; import { Url } from 'postman-collection'; -import * as urijs from 'urijs'; export function transformServer(url: Url): IServer | undefined { - const origin = urijs(url.toString()).origin(); - return origin ? { url: origin } : undefined; + try { + const origin = new URL(url.toString()).origin; + return origin ? { url: origin } : undefined; + } catch { + return undefined; + } } diff --git a/src/utils.ts b/src/utils.ts index e7c55533..4b0d5ea0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,57 +1,11 @@ -import { hasRef, isLocalRef, pathToPointer, pointerToPath, resolveInlineRef } from '@stoplight/json'; +import { hasRef, isLocalRef, resolveInlineRef } from '@stoplight/json'; import { Dictionary, Optional } from '@stoplight/types'; import { isObjectLike, map } from 'lodash'; -import * as URIJS from 'urijs'; export function mapToKeys(collection: Optional) { return map(collection, Object.keys); } -// wraps urijs to handle edge cases that would normally error out or cause unexpected behavior -// in all funcs set a default empty string, since URI.func() !== URI.func(undefined) [<- undefined throw error in most cases] -// in all funcs set a default empty string, since URI.func('') will reset fields back to null while URI.func() will not -export function URI(url: string | URI = '') { - const uri: URI = !url || typeof url === 'string' ? new URIJS(url) : url; - - return { - scheme: (type: string = '') => URI(uri.scheme(type)), - - // if host includes a port that is not valid (a non-number) we encode the host to avoid errors and have it stored under uri.hostname - host: (host: string = '') => - URI(uri.host(typeof host === 'string' && Number.isNaN(Number(host.split(':')[1])) ? URIJS.encode(host) : host)), - - // if we try to set port directly and it is invalid (a non-number) reset the port to null - port: (port: string = '') => URI(uri.port(Number.isNaN(Number(port)) ? '' : port)), - - path: (path: string = '') => URI(uri.path(path)), - - pointer: (pathOrPointer: string | string[]) => - URI(uri.hash(Array.isArray(pathOrPointer) ? pathToPointer(pathOrPointer) : pathOrPointer)), - - // decode uri since some of the built in uri functions encode the args, - // normalize the uri to remove extra slash characters, and have uniform capitzalization where appropriate (schemes) - toString: () => URIJS.decode(uri.normalize().valueOf()), - - append: (path: string = '') => { - const uri2 = new URIJS(path); - - if (uri.fragment()) { - if (uri2.fragment()) { - return URI(uri.fragment(uri2.fragment())); - } else { - return URI(uri.hash(pathToPointer([...pointerToPath(uri.hash()), path]))); - } - } else { - if (uri2.fragment()) { - return URI(uri.fragment(pathToPointer([path.split('#/')[1]]))); - } else { - return URI(uri.segment([...uri.segment(), path])); - } - } - }, - }; -} - export const isDictionary = (maybeDictionary: unknown): maybeDictionary is Dictionary => isObjectLike(maybeDictionary); diff --git a/yarn.lock b/yarn.lock index 9c94a623..07650032 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,11 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.10.1" + "@babel/highlight" "^7.16.7" "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.8.7" @@ -68,10 +68,10 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" - integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== +"@babel/helper-validator-identifier@^7.10.1", "@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== "@babel/helpers@^7.8.4": version "7.8.4" @@ -82,16 +82,16 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" -"@babel/highlight@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" - integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== +"@babel/highlight@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== dependencies: - "@babel/helper-validator-identifier" "^7.10.1" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": +"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== @@ -166,14 +166,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/runtime@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" - integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": +"@babel/template@^7.3.3", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== @@ -182,7 +175,7 @@ "@babel/parser" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== @@ -787,11 +780,13 @@ resolve "^1.14.1" "@rollup/pluginutils@^3.0.1": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.0.8.tgz#4e94d128d94b90699e517ef045422960d18c8fde" - integrity sha512-rYGeAc4sxcZ+kPG/Tw4/fwJODC3IXHYDH4qusdN/b6aLw5LPUbzpecYbEJh4sVQGPFJxd2dBU4kc1H3oy9/bnw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== dependencies: + "@types/estree" "0.0.39" estree-walker "^1.0.1" + picomatch "^2.2.2" "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" @@ -967,26 +962,26 @@ dependencies: eslint-config-prettier "^6.7.0" -"@stoplight/json@^3.10.2": - version "3.10.2" - resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.10.2.tgz#a17463c7576602971edbf6f11d3eaed80db262bb" - integrity sha512-mUf4t2bOBZjVa8KdEI4+EqX/8zXJLfGKv/cj++giLbCkYNGjBgJnmPSDNOOBgJHpocdVml0hK8ZuLADqWVVB2Q== +"@stoplight/json@^3.17.1": + version "3.17.1" + resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.17.1.tgz#17aa8f17c58678649cd0d345fb184e0bea16f8df" + integrity sha512-OQbjaPU9/VPQYa3zEfN82vjXu3vWv/537Ei7TX/xYLMPdpdBI/mCyThdZAG4SN1t3iho2dxIKpTVObuCTTgBvA== dependencies: - "@stoplight/ordered-object-literal" "^1.0.1" - "@stoplight/types" "^11.9.0" + "@stoplight/ordered-object-literal" "^1.0.2" + "@stoplight/types" "^12.3.0" jsonc-parser "~2.2.1" - lodash "^4.17.15" + lodash "^4.17.21" safe-stable-stringify "^1.1" -"@stoplight/ordered-object-literal@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.1.tgz#01ece81ba5dda199ca3dc5ec7464691efa5d5b76" - integrity sha512-kDcBIKwzAXZTkgzaiPXH2I0JXavBkOb3jFzYNFS5cBuvZS3s/K+knpk2wLVt0n8XrnRQsSffzN6XG9HqUhfq6Q== +"@stoplight/ordered-object-literal@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.2.tgz#2a88a5ebc8b68b54837ac9a9ae7b779cdd862062" + integrity sha512-0ZMS/9sNU3kVo/6RF3eAv7MK9DY8WLjiVJB/tVyfF2lhr2R4kqh534jZ0PlrFB9CRXrdndzn1DbX6ihKZXft2w== "@stoplight/scripts@^8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@stoplight/scripts/-/scripts-8.2.1.tgz#87a50fc849319aa6e401aea0a3b5c626b6e559f8" - integrity sha512-8SROPbbQZHrS0y56MLlIIF0GU4nWYS6Ghu3+wVvgLXV5KWkXbYazqH70Mfmpk6z9xl8ro+srLdCTAwtiH45+Bg== + version "8.2.4" + resolved "https://registry.yarnpkg.com/@stoplight/scripts/-/scripts-8.2.4.tgz#23a9289b18696525744cc4e8004e3382b1f2ddd8" + integrity sha512-BNv+qdeaSi6be5mOJitYVAZ3zNyY3B9Qq5IbUD9mnTOFJo3uQWPByciI8MnwZ7WYZX0gFyeLJ3zEDyJW3tnNyA== dependencies: "@commitlint/cli" "8.3.5" "@commitlint/config-conventional" "8.3.4" @@ -1003,6 +998,7 @@ commitizen "4.0.3" cz-conventional-changelog "3.1.x" esm "^3.2.25" + find-up "^4.1.0" husky "4.2.3" inquirer "7.0.4" lint-staged "10.0.7" @@ -1018,15 +1014,7 @@ resolved "https://registry.yarnpkg.com/@stoplight/test-utils/-/test-utils-0.0.1.tgz#b0d38c8a0abebda2dacbc2aa6a4f9bec44878e7b" integrity sha512-Cj3waLFR9bYLG8yvgkjXMxOfiVdewRyrKdH5RQpttVNfKj5UF+mElofPcHn/UfiOuxK3t5rbsdMfS26LK5tdQg== -"@stoplight/types@^11.9.0": - version "11.10.0" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-11.10.0.tgz#60bbee770526f4de9cd1c613c7ab84c4e4674126" - integrity sha512-ffHD9i4UHS8Gsg7Ar8pKjI9B4kq7MRekE7tCROFGcFDzQhfRx9T92AduoLAtP/010XNYq23yDKpLBRPIEk8+xg== - dependencies: - "@types/json-schema" "^7.0.4" - utility-types "^3.10.0" - -"@stoplight/types@^12.4.0": +"@stoplight/types@^12.3.0", "@stoplight/types@^12.4.0": version "12.4.0" resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.4.0.tgz#7a90e747d3f531742316b7b2969e6c45efd3e968" integrity sha512-ELf0dOdROaKEE0iw1YF9qbL9QjIVfdyiCoBPpUQmvQej9F7tQR/TsYMvyUb8kz+rd49u3hGP/sirVuaTKI1ZOg== @@ -1039,7 +1027,7 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" integrity sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA== -"@types/babel__core@^7.0.0": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.9" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== @@ -1050,17 +1038,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.7": - version "7.1.8" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.8.tgz#057f725aca3641f49fc11c7a87a9de5ec588a5d7" - integrity sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__generator@*": version "7.6.0" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.0.tgz#f1ec1c104d1bb463556ecb724018ab788d0c172a" @@ -1098,6 +1075,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11" integrity sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ== +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -1133,7 +1115,7 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/json-schema@*", "@types/json-schema@7.0.5", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4": +"@types/json-schema@7.0.5", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== @@ -1190,13 +1172,6 @@ resolved "https://registry.yarnpkg.com/@types/swagger-schema-official/-/swagger-schema-official-2.0.21.tgz#56812a86dcd57ba60e5c51705ee96a2b2dc9b374" integrity sha512-n9BbLOjR4Hre7B4TSGGMPohOgOg8tcp00uxqsIE00uuWQC0QuX57G1bqC1csLsk2DpTGtHkd0dEb3ipsCZ9dAA== -"@types/to-json-schema@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@types/to-json-schema/-/to-json-schema-0.2.0.tgz#6c76736449942aa8a16a522fa2d3fcfd3bcb8d15" - integrity sha512-9fqRjNFSSxJ8dQrE4v8gThS5ftxdFj8Q0y8hAjaF+uN+saJRxLiJdtFaDd9sv3bhzwcB2oDJpT/1ZelHnexbLw== - dependencies: - "@types/json-schema" "*" - "@types/type-is@^1.6.3": version "1.6.3" resolved "https://registry.yarnpkg.com/@types/type-is/-/type-is-1.6.3.tgz#45285b3be846a4afc9d488910a8e4b7bc2e8a169" @@ -1205,9 +1180,9 @@ "@types/node" "*" "@types/urijs@~1.19.9": - version "1.19.9" - resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.9.tgz#cedccc8e69677080cc8bb2e9ec50c6dd6093429e" - integrity sha512-/tiJyrc0GPcsReHzgC0SXwOmoPjLqYe01W7dLYB0yasQXMbcRee+ZIk+g8MIQhoBS8fPoBQO3Y93+aeBrI93Ug== + version "1.19.17" + resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.17.tgz#232ac9884b6a2aeab5dbe70b79cdb91d5067c325" + integrity sha512-ShIlp+8iNGo/yVVfYFoNRqUiaE9wMCzsSl85qTg2/C5l56BTJokU7QeMgVBQ9xhcyhWQP0zGXPBZPPvEG/sRmQ== "@types/yargs-parser@*": version "13.1.0" @@ -1341,9 +1316,9 @@ acorn-walk@^7.1.1: integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== acorn@^7.1.0, acorn@^7.1.1, acorn@^7.2.0: - version "7.3.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" - integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== agent-base@4, agent-base@^4.3.0: version "4.3.0" @@ -1381,17 +1356,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.10.2: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^6.5.5: +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: version "6.12.3" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== @@ -4398,6 +4363,13 @@ is-cidr@^3.0.0: dependencies: cidr-regex "^2.0.10" +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4688,20 +4660,7 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" - integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== - dependencies: - "@babel/core" "^7.7.5" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-instrument@^4.0.3: +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== @@ -5742,7 +5701,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.1: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6009,11 +5968,6 @@ micromatch@^3.0.4, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== - mime-db@1.49.0: version "1.49.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" @@ -6026,20 +5980,13 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.32: +mime-types@2.1.32, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.32" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: mime-db "1.49.0" -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== - dependencies: - mime-db "1.44.0" - mime@^2.4.3: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" @@ -6824,20 +6771,13 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" -p-limit@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== - dependencies: - p-try "^2.0.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -7027,10 +6967,10 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg== -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^2.0.0: version "2.0.0" @@ -7056,10 +6996,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.0.0: version "2.3.0" @@ -7326,6 +7266,13 @@ qw@~1.0.1: resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4" integrity sha1-77/cdA+a0FQwRCassYNBLMi5ltQ= +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -7517,11 +7464,6 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.4: - version "0.13.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -7681,11 +7623,13 @@ resolve-url@^0.2.1: integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.1, resolve@^1.17.0, resolve@^1.3.2: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + version "1.21.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== dependencies: - path-parse "^1.0.6" + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^2.0.0: version "2.0.0" @@ -7750,14 +7694,14 @@ rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: glob "^7.1.3" rollup-plugin-terser@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.0.tgz#9c0dd33d5771df9630cd027d6a2559187f65885e" - integrity sha512-XGMJihTIO3eIBsVGq7jiNYOdDMb3pVxuzY0uhOE/FM4x/u9nQgr3+McsjzqBn3QfHIpNSZmFnpoKAwHBEcsT7g== + version "5.3.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz#8c650062c22a8426c64268548957463bf981b413" + integrity sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w== dependencies: "@babel/code-frame" "^7.5.5" jest-worker "^24.9.0" rollup-pluginutils "^2.8.2" - serialize-javascript "^2.1.2" + serialize-javascript "^4.0.0" terser "^4.6.2" rollup-pluginutils@^2.8.2: @@ -7922,22 +7866,19 @@ semver@6.3.0, semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@7.3.5: +semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -serialize-javascript@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" - integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -8135,9 +8076,9 @@ source-map-resolve@^0.5.0: urix "^0.1.0" source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -8526,6 +8467,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -8599,9 +8545,9 @@ terminal-link@^2.0.0: supports-hyperlinks "^2.0.0" terser@^4.6.2: - version "4.6.6" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.6.tgz#da2382e6cafbdf86205e82fb9a115bd664d54863" - integrity sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g== + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: commander "^2.20.0" source-map "~0.6.1" @@ -9002,11 +8948,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urijs@~1.19.2: - version "1.19.4" - resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.4.tgz#3953d7dacd801336c17921ee8c5518b150cbf965" - integrity sha512-2YF/wdFu02Gsly/wyx+S/f5w/oCF0ihVSgK/Sn8fcY/ZYMYtqxgi03Vi3V7HqyQP8mj8xHMuNFTBIPufmPRdoA== - urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" @@ -9345,18 +9286,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: +yaml@^1.10.0, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^1.7.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.2.tgz#a29c03f578faafd57dcb27055f9a5d569cb0c3d9" - integrity sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg== - dependencies: - "@babel/runtime" "^7.8.7" - yargs-parser@18.x, yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" From 37ace29887168dbfc7847684b569ff25f84bc23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Thu, 24 Mar 2022 02:28:22 +0100 Subject: [PATCH 03/16] feat: setup package exports (#175) BREAKING CHANGE: defined package exports BREAKING CHANGE: Node >=14.13 is required --- .circleci/config.yml | 6 +- package.json | 31 +++- src/oas/index.ts | 1 + src/oas/transformers/index.ts | 2 + tsconfig.build.json | 2 +- yarn.lock | 294 ++++++++++++++++++++++++---------- 6 files changed, 247 insertions(+), 89 deletions(-) create mode 100644 src/oas/transformers/index.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index b3eea75f..14989f36 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 jobs: test: docker: - - image: circleci/node:10 + - image: circleci/node:lts steps: - checkout - run: yarn @@ -11,7 +11,7 @@ jobs: - run: yarn test --maxWorkers=2 build: docker: - - image: circleci/node:10 + - image: circleci/node:lts steps: - checkout - run: yarn @@ -19,7 +19,7 @@ jobs: release: docker: - - image: circleci/node:10 + - image: circleci/node:lts steps: - checkout - run: yarn diff --git a/package.json b/package.json index 9b72a915..9eb76068 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,36 @@ "url": "https://github.com/stoplightio/http-spec" }, "license": "Apache-2.0", - "main": "src/index.ts", "files": [ "**/*" ], "engines": { - "node": ">=10" + "node": ">=14.13" + }, + "type": "commonjs", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "./oas": { + "types": "./oas/index.d.ts", + "default": "./oas/index.js" + }, + "./oas2": { + "types": "./oas2/index.d.ts", + "default": "./oas2/index.js" + }, + "./oas3": { + "types": "./oas3/index.d.ts", + "default": "./oas3/index.js" + }, + "./postman": { + "types": "./postman/index.d.ts", + "default": "./postman/index.js" + } }, "scripts": { "build": "sl-scripts build", @@ -38,11 +62,12 @@ "lodash": "^4.17.15", "openapi3-ts": "^2.0.1", "postman-collection": "^4.1.0", + "tslib": "^2.3.1", "type-is": "^1.6.18" }, "devDependencies": { "@stoplight/eslint-config": "^1.1.0", - "@stoplight/scripts": "^8.2.1", + "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", "@types/json-schema": "7.0.5", diff --git a/src/oas/index.ts b/src/oas/index.ts index 51202e81..ca4dfd22 100644 --- a/src/oas/index.ts +++ b/src/oas/index.ts @@ -1 +1,2 @@ export * from './operation'; +export { translateSchemaObject } from './transformers/index'; diff --git a/src/oas/transformers/index.ts b/src/oas/transformers/index.ts new file mode 100644 index 00000000..d0152846 --- /dev/null +++ b/src/oas/transformers/index.ts @@ -0,0 +1,2 @@ +export { translateSchemaObject } from './schema/index'; +export { translateLogo } from './translateLogo'; diff --git a/tsconfig.build.json b/tsconfig.build.json index 5c7b3e34..2efb299d 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -12,6 +12,6 @@ "compilerOptions": { "outDir": "dist", "moduleResolution": "node", - "target": "es6" + "target": "ES2019" } } diff --git a/yarn.lock b/yarn.lock index 07650032..2115dc9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== @@ -771,15 +771,47 @@ dependencies: "@types/node" ">= 8" -"@rollup/plugin-typescript@^3.0.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-3.1.1.tgz#a39175a552ed82a3e424862e6bb403bf9da451ee" - integrity sha512-VPY1MbzIJT+obpav9Kns4MlipVJ1FuefwzO4s1uCVXAzVWya+bhhNauOmmqR/hy1zj7tePfh3t9iBN+HbIzyRA== +"@rollup/plugin-commonjs@^19.0.0": + version "19.0.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.2.tgz#1ccc3d63878d1bc9846f8969f09dd3b3e4ecc244" + integrity sha512-gBjarfqlC7qs0AutpRW/hrFNm+cd2/QKxhwyFa+srbg1oX7rDsEU3l+W7LAUhsAp9mPJMAkXDhLbQaVwEaE8bA== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== dependencies: - "@rollup/pluginutils" "^3.0.1" - resolve "^1.14.1" + "@rollup/pluginutils" "^3.0.8" -"@rollup/pluginutils@^3.0.1": +"@rollup/plugin-node-resolve@^13.0.6": + version "13.1.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" + integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/pluginutils@4.x", "@rollup/pluginutils@^4.1.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08" + integrity sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== @@ -978,17 +1010,20 @@ resolved "https://registry.yarnpkg.com/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.2.tgz#2a88a5ebc8b68b54837ac9a9ae7b779cdd862062" integrity sha512-0ZMS/9sNU3kVo/6RF3eAv7MK9DY8WLjiVJB/tVyfF2lhr2R4kqh534jZ0PlrFB9CRXrdndzn1DbX6ihKZXft2w== -"@stoplight/scripts@^8.2.1": - version "8.2.4" - resolved "https://registry.yarnpkg.com/@stoplight/scripts/-/scripts-8.2.4.tgz#23a9289b18696525744cc4e8004e3382b1f2ddd8" - integrity sha512-BNv+qdeaSi6be5mOJitYVAZ3zNyY3B9Qq5IbUD9mnTOFJo3uQWPByciI8MnwZ7WYZX0gFyeLJ3zEDyJW3tnNyA== +"@stoplight/scripts@^9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@stoplight/scripts/-/scripts-9.2.0.tgz#a89007cdcfa958b02f49d038730ba87d52fbc205" + integrity sha512-Jh6L6sf0fCaHjohHGk2k/5ZHheFKoQSzw8mOxNXcnqKH6oBiuWo37QkCVmgHya6jXpHjeGAjYZilUz9i43Uhng== dependencies: "@commitlint/cli" "8.3.5" "@commitlint/config-conventional" "8.3.4" "@oclif/command" "1.5.19" "@oclif/config" "1.14.0" "@oclif/plugin-help" "2.2.3" - "@rollup/plugin-typescript" "^3.0.0" + "@rollup/plugin-commonjs" "^19.0.0" + "@rollup/plugin-json" "^4.1.0" + "@rollup/plugin-node-resolve" "^13.0.6" + "@rollup/pluginutils" "4.x" "@semantic-release/commit-analyzer" "8.0.1" "@semantic-release/git" "9.0.0" "@semantic-release/github" "7.0.3" @@ -998,16 +1033,19 @@ commitizen "4.0.3" cz-conventional-changelog "3.1.x" esm "^3.2.25" + estree-walker "2.x" find-up "^4.1.0" husky "4.2.3" inquirer "7.0.4" lint-staged "10.0.7" + magic-string "0.25.x" rimraf "3.0.2" - rollup "^1.31.1" - rollup-plugin-terser "^5.2.0" + rollup "^2.47.0" + rollup-plugin-terser "^7.0.2" + rollup-plugin-typescript2 "^0.30.0" semantic-release "17.0.3" shelljs "0.8.x" - tslib "1.10.0" + tslib "^2.2.0" "@stoplight/test-utils@^0.0.1": version "0.0.1" @@ -1157,6 +1195,13 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.1.tgz#b6e98083f13faa1e5231bfa3bdb1b0feff536b6d" integrity sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ== +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + "@types/retry@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -1315,11 +1360,16 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@^7.1.0, acorn@^7.1.1, acorn@^7.2.0: +acorn@^7.1.1, acorn@^7.2.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -1820,6 +1870,11 @@ buffer-from@1.x, buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +builtin-modules@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -2287,6 +2342,11 @@ commitizen@4.0.3, commitizen@^4.0.3: strip-bom "4.0.0" strip-json-comments "3.0.1" +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + compare-func@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648" @@ -3195,10 +3255,10 @@ estraverse@^5.1.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== -estree-walker@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" - integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== +estree-walker@2.x, estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== estree-walker@^1.0.1: version "1.0.1" @@ -3474,6 +3534,15 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-node-modules@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-2.0.0.tgz#5db1fb9e668a3d451db3d618cd167cdd59e41b69" @@ -3649,6 +3718,11 @@ fsevents@^2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4363,7 +4437,7 @@ is-cidr@^3.0.0: dependencies: cidr-regex "^2.0.10" -is-core-module@^2.8.0: +is-core-module@^2.2.0, is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== @@ -4466,6 +4540,11 @@ is-installed-globally@^0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" @@ -4536,6 +4615,13 @@ is-redirect@^1.0.0: resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + is-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" @@ -5059,14 +5145,6 @@ jest-watcher@^26.1.0: jest-util "^26.1.0" string-length "^4.0.1" -jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== - dependencies: - merge-stream "^2.0.0" - supports-color "^6.1.0" - jest-worker@^26.1.0: version "26.1.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.1.0.tgz#65d5641af74e08ccd561c240e7db61284f82f33d" @@ -5075,6 +5153,15 @@ jest-worker@^26.1.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + jest@26.1.0: version "26.1.0" resolved "https://registry.yarnpkg.com/jest/-/jest-26.1.0.tgz#2f3aa7bcffb9bfd025473f83bbbf46a3af026263" @@ -5781,6 +5868,13 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== +magic-string@0.25.x, magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -5795,6 +5889,13 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@1.x, make-error@^1.1.1: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" @@ -5968,10 +6069,10 @@ micromatch@^3.0.4, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@1.49.0: - version "1.49.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" - integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== +mime-db@1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== mime-format@2.0.1: version "2.0.1" @@ -5980,7 +6081,14 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.32, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@2.1.34: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== + dependencies: + mime-db "1.51.0" + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.32" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== @@ -6967,7 +7075,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg== -path-parse@^1.0.7: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -7033,7 +7141,7 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" -pkg-dir@^4.2.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -7053,9 +7161,9 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postman-collection@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.0.tgz#c5833aa3cb82df79cc5d16e5d7399c71a84ea4fa" - integrity sha512-J9IpCMXpGDLN7MGhdMcUbZ0SIWLCcTVdrjTgKVYubkW1sn1KcDqJgsdTr/ItkO8dOXKLuhvnq2QnE5Vrzb3WMA== + version "4.1.1" + resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.1.tgz#aba50dfca3c3ce6881b9598958c6a52eec3959d9" + integrity sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg== dependencies: faker "5.5.3" file-type "3.9.0" @@ -7064,7 +7172,7 @@ postman-collection@^4.1.0: liquid-json "0.3.1" lodash "4.17.21" mime-format "2.0.1" - mime-types "2.1.32" + mime-types "2.1.34" postman-url-encoder "3.0.5" semver "7.3.5" uuid "8.3.2" @@ -7622,7 +7730,15 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.1, resolve@^1.17.0, resolve@^1.3.2: +resolve@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: version "1.21.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== @@ -7631,6 +7747,15 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.1, resolve@^1.17 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.19.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -7693,32 +7818,33 @@ rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: dependencies: glob "^7.1.3" -rollup-plugin-terser@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz#8c650062c22a8426c64268548957463bf981b413" - integrity sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w== +rollup-plugin-terser@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== dependencies: - "@babel/code-frame" "^7.5.5" - jest-worker "^24.9.0" - rollup-pluginutils "^2.8.2" + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" serialize-javascript "^4.0.0" - terser "^4.6.2" + terser "^5.0.0" -rollup-pluginutils@^2.8.2: - version "2.8.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" - integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== +rollup-plugin-typescript2@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.30.0.tgz#1cc99ac2309bf4b9d0a3ebdbc2002aecd56083d3" + integrity sha512-NUFszIQyhgDdhRS9ya/VEmsnpTe+GERDMmFo0Y+kf8ds51Xy57nPNGglJY+W6x1vcouA7Au7nsTgsLFj2I0PxQ== dependencies: - estree-walker "^0.6.1" + "@rollup/pluginutils" "^4.1.0" + find-cache-dir "^3.3.1" + fs-extra "8.1.0" + resolve "1.20.0" + tslib "2.1.0" -rollup@^1.31.1: - version "1.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4" - integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A== - dependencies: - "@types/estree" "*" - "@types/node" "*" - acorn "^7.1.0" +rollup@^2.47.0: + version "2.70.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.1.tgz#824b1f1f879ea396db30b0fc3ae8d2fead93523e" + integrity sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA== + optionalDependencies: + fsevents "~2.3.2" rsvp@^4.8.4: version "4.8.5" @@ -8075,7 +8201,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -8098,11 +8224,16 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: +source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + spawn-error-forwarder@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz#1afd94738e999b0346d7b9fc373be55e07577029" @@ -8437,13 +8568,6 @@ supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^7.0.0, supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" @@ -8544,14 +8668,15 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" -terser@^4.6.2: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== +terser@^5.0.0: + version "5.12.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.12.1.tgz#4cf2ebed1f5bceef5c83b9f60104ac4a78b49e9c" + integrity sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ== dependencies: + acorn "^8.5.0" commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map "~0.7.2" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -8737,16 +8862,21 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tslib@^2.2.0, tslib@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" From afb81488aa16ed299be35d3f29793f49a6055c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Thu, 24 Mar 2022 02:25:53 +0100 Subject: [PATCH 04/16] chore: bump deps --- package.json | 26 +- .../transformers/__tests__/servers.test.ts | 2 +- src/oas2/transformers/params.ts | 12 +- .../transformers/__tests__/content.test.ts | 4 +- src/oas3/transformers/content.ts | 20 +- src/postman/index.ts | 2 +- .../transformers/__tests__/response.test.ts | 2 +- .../__tests__/securitySchemes.spec.ts | 4 +- yarn.lock | 1285 +++++++++-------- 9 files changed, 719 insertions(+), 638 deletions(-) diff --git a/package.json b/package.json index 9eb76068..64a29b26 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "test": "jest" }, "dependencies": { - "@stoplight/json": "^3.17.1", + "@stoplight/json": "^3.17.2", "@stoplight/types": "^12.4.0", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", @@ -66,27 +66,27 @@ "type-is": "^1.6.18" }, "devDependencies": { - "@stoplight/eslint-config": "^1.1.0", + "@stoplight/eslint-config": "^3.0.0", "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", "@types/json-schema": "7.0.5", "@types/lodash": "4.14.157", "@types/postman-collection": "^3.5.3", - "@typescript-eslint/eslint-plugin": "^3.5.0", - "@typescript-eslint/parser": "^3.5.0", - "eslint": "^7.3.1", - "eslint-plugin-import": "^2.22.0", - "eslint-plugin-jest": "^23.17.1", - "eslint-plugin-prettier": "^3.1.4", - "eslint-plugin-react": "^7.20.3", - "eslint-plugin-react-hooks": "^4.0.5", - "eslint-plugin-simple-import-sort": "^5.0.3", + "@typescript-eslint/eslint-plugin": "^5.15.0", + "@typescript-eslint/parser": "^5.15.0", + "eslint": "^8.11.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jest": "^26.1.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-simple-import-sort": "^7.0.0", "jest": "26.1.0", - "prettier": "^2.0.5", + "prettier": "^2.6.0", "ts-jest": "26.1.1", "ts-node": "^8.10.2", - "typescript": "3.9.6", + "typescript": "4.6.2", "utility-types": "^3.10.0" }, "lint-staged": { diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 1950bdee..85d52cee 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,6 +1,6 @@ import { translateToServers } from '../servers'; -type GlobalWithLocation = typeof global & { location: Partial & { href: string } }; +type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; describe('translateToServers', () => { afterAll(() => { diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index 19ec76d6..7527594f 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -56,12 +56,12 @@ function chooseQueryParameterStyle( } export function translateToHeaderParam(document: DeepPartial, parameter: HeaderParameter): IHttpHeaderParam { - return (pickBy({ + return pickBy({ ...buildSchemaForParameter(document, parameter), name: parameter.name, style: HttpParamStyles.Simple, required: parameter.required, - }) as unknown) as IHttpHeaderParam; + }) as unknown as IHttpHeaderParam; } export function translateToHeaderParams( @@ -179,22 +179,22 @@ function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { } export function translateToQueryParameter(document: DeepPartial, query: QueryParameter): IHttpQueryParam { - return (pickBy({ + return pickBy({ ...buildSchemaForParameter(document, query), allowEmptyValue: query.allowEmptyValue, name: query.name, style: chooseQueryParameterStyle(query), required: query.required, - }) as unknown) as IHttpQueryParam; + }) as unknown as IHttpQueryParam; } export function translateToPathParameter(document: DeepPartial, parameter: PathParameter): IHttpPathParam { - return (pickBy({ + return pickBy({ ...buildSchemaForParameter(document, parameter), name: parameter.name, style: HttpParamStyles.Simple, required: parameter.required, - }) as unknown) as IHttpPathParam; + }) as unknown as IHttpPathParam; } function buildSchemaForParameter( diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index a823e103..05fef5b7 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -322,11 +322,11 @@ describe('translateMediaTypeObject', () => { describe('schema invalid', () => { it('type as array does not throw error', () => { - const schema = ({ + const schema = { type: ['string', 'object'], description: 'A simple string', example: 'hello', - } as unknown) as SchemaObject; + } as unknown as SchemaObject; expect(() => translateMediaTypeObject( diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index fe2c4815..696e31a1 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -97,12 +97,12 @@ export function translateHeaderObject(headerObject: unknown, name: string): Opti }); } - return (pickBy({ + return pickBy({ ...baseContent, schema: get(headerObject, 'schema') as any, encodings, examples, - }) as unknown) as IHttpHeaderParam; + }) as unknown as IHttpHeaderParam; } export function translateMediaTypeObject( @@ -165,11 +165,13 @@ function resolveMediaObject(document: DeepPartial, maybeMediaObje return mediaObject; } -const transformExamples = (source: MediaTypeObject | HeaderObject) => (key: string): INodeExample => { - return { - summary: get(source, ['examples', key, 'summary']), - description: get(source, ['examples', key, 'description']), - value: get(source, ['examples', key, 'value']), - key, +const transformExamples = + (source: MediaTypeObject | HeaderObject) => + (key: string): INodeExample => { + return { + summary: get(source, ['examples', key, 'summary']), + description: get(source, ['examples', key, 'description']), + value: get(source, ['examples', key, 'value']), + key, + }; }; -}; diff --git a/src/postman/index.ts b/src/postman/index.ts index bcba0497..f2e98a7a 100644 --- a/src/postman/index.ts +++ b/src/postman/index.ts @@ -1,2 +1,2 @@ -export * from './service'; export * from './operation'; +export * from './service'; diff --git a/src/postman/transformers/__tests__/response.test.ts b/src/postman/transformers/__tests__/response.test.ts index 4078a5fe..93cb7ea4 100644 --- a/src/postman/transformers/__tests__/response.test.ts +++ b/src/postman/transformers/__tests__/response.test.ts @@ -275,7 +275,7 @@ describe('transformResponse()', () => { value: 'functions', domain: 'example.com', path: '/', - expires: (1502442248 as unknown) as string, // @todo remove after postman-collection types fix + expires: 1502442248 as unknown as string, // @todo remove after postman-collection types fix }, ], responseTime: 100, diff --git a/src/postman/transformers/__tests__/securitySchemes.spec.ts b/src/postman/transformers/__tests__/securitySchemes.spec.ts index 37c9cddf..13e0828a 100644 --- a/src/postman/transformers/__tests__/securitySchemes.spec.ts +++ b/src/postman/transformers/__tests__/securitySchemes.spec.ts @@ -609,7 +609,7 @@ describe('transformSecurityScheme()', () => { it('ignores unknown auth type', () => { expect( transformSecurityScheme( - new RequestAuth(({ type: 'non-existing-type' } as unknown) as RequestAuthDefinition), + new RequestAuth({ type: 'non-existing-type' } as unknown as RequestAuthDefinition), () => 'a', ), ).toBeUndefined(); @@ -790,7 +790,7 @@ describe.each<[string, PostmanSecurityScheme, PostmanSecurityScheme, boolean]>([ true, ], ])('given %s security schemes', (desc, scheme1, scheme2, result) => { - test(`returns ${result ? 'true' : 'false'}`, () => { + it(`returns ${result ? 'true' : 'false'}`, () => { expect(isPostmanSecuritySchemeEqual(scheme1, scheme2)).toEqual(result); }); }); diff --git a/yarn.lock b/yarn.lock index 2115dc9b..b0e76aba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -342,6 +342,35 @@ dependencies: find-up "^4.0.0" +"@eslint/eslintrc@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" + integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@humanwhocodes/config-array@^0.9.2": + version "0.9.5" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" + integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + "@iarna/cli@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641" @@ -987,17 +1016,17 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@stoplight/eslint-config@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@stoplight/eslint-config/-/eslint-config-1.1.0.tgz#9aa129235c716eecd3433611441b2b816791e588" - integrity sha512-pXUmmLhRL6292zLB1+4NLG+HnaH0rMaH8xlsPJSr/31DFmO1jSi3igjz7kEp3/ghyPgye0TSqRztUPBgkHSlLA== +"@stoplight/eslint-config@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@stoplight/eslint-config/-/eslint-config-3.0.0.tgz#455f6d12d2046aa991f5524c92b31567d7b2848f" + integrity sha512-4o3YUi03dDeenlr1qH0/L4tAad3koOuM0WD0jBWoCLxL+EJ7AZzceBX0fRO7LTzWJdCSHASUVd6lYQSeZMHwsQ== dependencies: - eslint-config-prettier "^6.7.0" + eslint-config-prettier "^8.3.0" -"@stoplight/json@^3.17.1": - version "3.17.1" - resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.17.1.tgz#17aa8f17c58678649cd0d345fb184e0bea16f8df" - integrity sha512-OQbjaPU9/VPQYa3zEfN82vjXu3vWv/537Ei7TX/xYLMPdpdBI/mCyThdZAG4SN1t3iho2dxIKpTVObuCTTgBvA== +"@stoplight/json@^3.17.2": + version "3.17.2" + resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.17.2.tgz#b086322615f5b262e2bed1271511808fc8a04f4f" + integrity sha512-NwIVzanXRUy291J5BMkncCZRMG1Lx+aq+VidGQgfkJjgo8vh1Y/PSAz7fSU8gVGSZBCcqmOkMI7R4zw7DlfTwA== dependencies: "@stoplight/ordered-object-literal" "^1.0.2" "@stoplight/types" "^12.3.0" @@ -1103,11 +1132,6 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - "@types/estree@*": version "0.0.42" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11" @@ -1153,11 +1177,16 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/json-schema@7.0.5", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4": +"@types/json-schema@7.0.5": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== +"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.9": + version "7.0.10" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" + integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -1241,88 +1270,85 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.5.0.tgz#e7736e0808b5fb947a5f9dd949ae6736a7226b84" - integrity sha512-m4erZ8AkSjoIUOf8s4k2V1xdL2c1Vy0D3dN6/jC9d7+nEqjY3gxXCkgi3gW/GAxPaA4hV8biaCoTVdQmfAeTCQ== +"@typescript-eslint/eslint-plugin@^5.15.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" + integrity sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw== dependencies: - "@typescript-eslint/experimental-utils" "3.5.0" - debug "^4.1.1" + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/type-utils" "5.16.0" + "@typescript-eslint/utils" "5.16.0" + debug "^4.3.2" functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.5.0.tgz#d09f9ffb890d1b15a7ffa9975fae92eee05597c4" - integrity sha512-zGNOrVi5Wz0jcjUnFZ6QUD0MCox5hBuVwemGCew2qJzUX5xPoyR+0EzS5qD5qQXL/vnQ8Eu+nv03tpeFRwLrDg== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/types" "3.5.0" - "@typescript-eslint/typescript-estree" "3.5.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/experimental-utils@^2.5.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.5.0.tgz#9ff8c11877c48df24e10e19d7bf542ee0359500d" - integrity sha512-sU07VbYB70WZHtgOjH/qfAp1+OwaWgrvD1Km1VXqRpcVxt971PMTU7gJtlrCje0M+Sdz7xKAbtiyIu+Y6QdnVA== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.5.0" - "@typescript-eslint/types" "3.5.0" - "@typescript-eslint/typescript-estree" "3.5.0" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/types@3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.5.0.tgz#4e3d2a2272268d8ec3e3e4a37152a64956682639" - integrity sha512-Dreqb5idi66VVs1QkbAwVeDmdJG+sDtofJtKwKCZXIaBsINuCN7Jv5eDIHrS0hFMMiOvPH9UuOs4splW0iZe4Q== - -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/typescript-estree@3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.5.0.tgz#dfc895db21a381b84f24c2a719f5bf9c600dcfdc" - integrity sha512-Na71ezI6QP5WVR4EHxwcBJgYiD+Sre9BZO5iJK2QhrmRPo/42+b0no/HZIrdD1sjghzlYv7t+7Jis05M1uMxQg== - dependencies: - "@typescript-eslint/types" "3.5.0" - "@typescript-eslint/visitor-keys" "3.5.0" - debug "^4.1.1" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/visitor-keys@3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.5.0.tgz#73c1ea2582f814735e4afdc1cf6f5e3af78db60a" - integrity sha512-7cTp9rcX2sz9Z+zua9MCOX4cqp5rYyFD5o8LlbSpXrMTXoRdngTtotRZEkm8+FNMHPWYFhitFK+qt/brK8BVJQ== - dependencies: - eslint-visitor-keys "^1.1.0" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.15.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.16.0.tgz#e4de1bde4b4dad5b6124d3da227347616ed55508" + integrity sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA== + dependencies: + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/typescript-estree" "5.16.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz#7e7909d64bd0c4d8aef629cdc764b9d3e1d3a69a" + integrity sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ== + dependencies: + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/visitor-keys" "5.16.0" + +"@typescript-eslint/type-utils@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz#b482bdde1d7d7c0c7080f7f2f67ea9580b9e0692" + integrity sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ== + dependencies: + "@typescript-eslint/utils" "5.16.0" + debug "^4.3.2" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.16.0.tgz#5827b011982950ed350f075eaecb7f47d3c643ee" + integrity sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g== + +"@typescript-eslint/typescript-estree@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz#32259459ec62f5feddca66adc695342f30101f61" + integrity sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ== + dependencies: + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/visitor-keys" "5.16.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.16.0", "@typescript-eslint/utils@^5.10.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.16.0.tgz#42218b459d6d66418a4eb199a382bdc261650679" + integrity sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/typescript-estree" "5.16.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz#f27dc3b943e6317264c7492e390c6844cd4efbbb" + integrity sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g== + dependencies: + "@typescript-eslint/types" "5.16.0" + eslint-visitor-keys "^3.0.0" JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: version "1.3.5" @@ -1350,22 +1376,22 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@^7.1.1, acorn@^7.2.0: +acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.5.0: +acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== @@ -1406,10 +1432,10 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: - version "6.12.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" - integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== +ajv@^6.10.0, ajv@^6.12.4, ajv@^6.5.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -1423,11 +1449,6 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" -ansi-colors@^3.2.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -1455,10 +1476,10 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^2.2.1: version "2.2.1" @@ -1546,6 +1567,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + argv-formatter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/argv-formatter/-/argv-formatter-1.0.0.tgz#a0ca0cbc29a5b73e836eebe1cbf6c5e0e4eb82f9" @@ -1576,14 +1602,16 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" - integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== +array-includes@^3.1.3, array-includes@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0" - is-string "^1.0.5" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + is-string "^1.0.7" array-union@^2.1.0: version "2.1.0" @@ -1595,22 +1623,23 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.flat@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" - integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== +array.prototype.flat@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" + es-abstract "^1.19.0" -array.prototype.flatmap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443" - integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg== +array.prototype.flatmap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" + integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" + es-abstract "^1.19.0" arrify@^1.0.1: version "1.0.1" @@ -1639,11 +1668,6 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1931,6 +1955,14 @@ cachedir@2.2.0: resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.2.0.tgz#19afa4305e05d79e417566882e0c8f960f62ff0e" integrity sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ== +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + call-limit@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.1.tgz#ef15f2670db3f1992557e2d965abc459e6e358d4" @@ -2405,11 +2437,6 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control- resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= - conventional-changelog-angular@^1.3.3: version "1.6.6" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz#b27f2b315c16d0a1f23eb181309d0e6a4698ea0f" @@ -2681,12 +2708,12 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: - ms "^2.1.1" + ms "2.1.2" debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -2695,10 +2722,10 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^3.1.0, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" @@ -2757,7 +2784,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.2, define-properties@^1.1.3: +define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -2856,14 +2883,6 @@ dir-glob@^3.0.0, dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2968,13 +2987,6 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enquirer@^2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381" - integrity sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA== - dependencies: - ansi-colors "^3.2.1" - env-ci@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-5.0.2.tgz#48b6687f8af8cdf5e31b8fcf2987553d085249d9" @@ -3007,22 +3019,31 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: - version "1.17.6" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" - integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== +es-abstract@^1.17.0-next.1, es-abstract@^1.19.0, es-abstract@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== dependencies: + call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.0" - is-regex "^1.1.0" - object-inspect "^1.7.0" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" @@ -3055,6 +3076,11 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@^1.14.1: version "1.14.2" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.2.tgz#14ab71bf5026c2aa08173afba22c6f3173284a84" @@ -3067,148 +3093,161 @@ escodegen@^1.14.1: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^6.7.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" - integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== - dependencies: - get-stdin "^6.0.0" +eslint-config-prettier@^8.3.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== -eslint-import-resolver-node@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" - integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: - debug "^2.6.9" - resolve "^1.13.1" + debug "^3.2.7" + resolve "^1.20.0" -eslint-module-utils@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" - integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== +eslint-module-utils@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: - debug "^2.6.9" - pkg-dir "^2.0.0" + debug "^3.2.7" + find-up "^2.1.0" -eslint-plugin-import@^2.22.0: - version "2.22.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" - integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== +eslint-plugin-import@^2.25.4: + version "2.25.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" + integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== dependencies: - array-includes "^3.1.1" - array.prototype.flat "^1.2.3" - contains-path "^0.1.0" + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" debug "^2.6.9" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.3" - eslint-module-utils "^2.6.0" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.2" has "^1.0.3" + is-core-module "^2.8.0" + is-glob "^4.0.3" minimatch "^3.0.4" - object.values "^1.1.1" - read-pkg-up "^2.0.0" - resolve "^1.17.0" - tsconfig-paths "^3.9.0" + object.values "^1.1.5" + resolve "^1.20.0" + tsconfig-paths "^3.12.0" -eslint-plugin-jest@^23.17.1: - version "23.17.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.17.1.tgz#c0f39ba78e0f33b7ee1ce4ec92b773e39026ea3f" - integrity sha512-/o36fw67qNbJGWbSBIBMfseMsNP/d88WUHAGHCi1xFwsNB3XXZGdvxbOw49j3iQz6MCW/yw8OeOsuQhi6mM5ZA== +eslint-plugin-jest@^26.1.1: + version "26.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.2.tgz#0f1a15c62889fffc3f78a773749d672f1bedb15f" + integrity sha512-1bXCoRODPkGN06n9KAMls4Jm0eyS+0Q/LWcIxhqWR2ycV0Z7lnx2c10idk4dtFIJY5xStgiIr5snC6/rxcXpbw== dependencies: - "@typescript-eslint/experimental-utils" "^2.5.0" + "@typescript-eslint/utils" "^5.10.0" -eslint-plugin-prettier@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2" - integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg== +eslint-plugin-prettier@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-react-hooks@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.0.5.tgz#4879003aa38e5d05d0312175beb6e4a1f617bfcf" - integrity sha512-3YLSjoArsE2rUwL8li4Yxx1SUg3DQWp+78N3bcJQGWVZckcp+yeQGsap/MSq05+thJk57o+Ww4PtZukXGL02TQ== +eslint-plugin-react-hooks@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" + integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== -eslint-plugin-react@^7.20.3: - version "7.20.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.20.3.tgz#0590525e7eb83890ce71f73c2cf836284ad8c2f1" - integrity sha512-txbo090buDeyV0ugF3YMWrzLIUqpYTsWSDZV9xLSmExE1P/Kmgg9++PD931r+KEWS66O1c9R4srLVVHmeHpoAg== +eslint-plugin-react@^7.29.4: + version "7.29.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" + integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== dependencies: - array-includes "^3.1.1" - array.prototype.flatmap "^1.2.3" + array-includes "^3.1.4" + array.prototype.flatmap "^1.2.5" doctrine "^2.1.0" - has "^1.0.3" - jsx-ast-utils "^2.4.1" - object.entries "^1.1.2" - object.fromentries "^2.0.2" - object.values "^1.1.1" - prop-types "^15.7.2" - resolve "^1.17.0" - string.prototype.matchall "^4.0.2" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.5" + object.fromentries "^2.0.5" + object.hasown "^1.1.0" + object.values "^1.1.5" + prop-types "^15.8.1" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.6" -eslint-plugin-simple-import-sort@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-5.0.3.tgz#9ae258ddada6efffc55e47a134afbd279eb31fc6" - integrity sha512-1rf3AWiHeWNCQdAq0iXNnlccnH1UDnelGgrPbjBBHE8d2hXVtOudcmy0vTF4hri3iJ0MKz8jBhmH6lJ0ZWZLHQ== +eslint-plugin-simple-import-sort@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz#a1dad262f46d2184a90095a60c66fef74727f0f8" + integrity sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw== -eslint-scope@^5.0.0, eslint-scope@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" - integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" - integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: - eslint-visitor-keys "^1.1.0" + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz#74415ac884874495f78ec2a97349525344c981fa" - integrity sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ== +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" -eslint@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.3.1.tgz#76392bd7e44468d046149ba128d1566c59acbe19" - integrity sha512-cQC/xj9bhWUcyi/RuMbRtC3I0eW8MH0jhRELSvpKYkWep3C6YZ2OkvcvJVUeO6gcunABmzptbXBuDoXsjHmfTA== +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.11.0.tgz#88b91cfba1356fc10bb9eb592958457dfe09fb37" + integrity sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA== dependencies: - "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^1.2.1" + "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" - eslint-scope "^5.1.0" - eslint-utils "^2.0.0" - eslint-visitor-keys "^1.2.0" - espree "^7.1.0" - esquery "^1.2.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" - ignore "^4.0.6" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.14" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" + regexpp "^3.2.0" + strip-ansi "^6.0.1" strip-json-comments "^3.1.0" - table "^5.2.3" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -3217,43 +3256,43 @@ esm@^3.2.25: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.1.0.tgz#a9c7f18a752056735bf1ba14cb1b70adc3a5ce1c" - integrity sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw== +espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== dependencies: - acorn "^7.2.0" - acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.2.0" + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" - integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== estree-walker@2.x, estree-walker@^2.0.1: version "2.0.2" @@ -3432,7 +3471,7 @@ faker@5.5.3: resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -3442,17 +3481,16 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.1.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" - integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" + glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" + micromatch "^4.0.4" fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" @@ -3505,12 +3543,12 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-type@3.9.0: version "3.9.0" @@ -3600,19 +3638,18 @@ findup-sync@^3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flush-write-stream@^1.0.0: version "1.1.1" @@ -3713,12 +3750,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== - -fsevents@~2.3.2: +fsevents@^2.1.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3784,6 +3816,15 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz#6f7764f88ea11e0b514bd9bd860a132259992ca4" @@ -3794,11 +3835,6 @@ get-stdin@7.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== - get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -3818,6 +3854,14 @@ get-stream@^5.0.0: dependencies: pump "^3.0.0" +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3853,13 +3897,20 @@ git-raw-commits@^2.0.0: split2 "^2.0.0" through2 "^3.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob@7.1.4: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" @@ -3916,23 +3967,23 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== +globals@^13.6.0, globals@^13.9.0: + version "13.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== dependencies: - type-fest "^0.8.1" + type-fest "^0.20.2" -globby@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154" - integrity sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg== +globby@^11.0.0, globby@^11.0.4: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" slash "^3.0.0" got@^6.7.1: @@ -3994,6 +4045,11 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" @@ -4009,10 +4065,17 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.0, has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" has-unicode@^2.0.0, has-unicode@~2.0.1: version "2.0.1" @@ -4209,15 +4272,10 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" - integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== +ignore@^5.1.8, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== import-fresh@^2.0.0: version "2.0.0" @@ -4227,10 +4285,10 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -4345,14 +4403,14 @@ inquirer@7.0.4: strip-ansi "^5.1.0" through "^2.3.6" -internal-slot@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3" - integrity sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g== +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== dependencies: - es-abstract "^1.17.0-next.1" + get-intrinsic "^1.1.0" has "^1.0.3" - side-channel "^1.0.2" + side-channel "^1.0.4" interpret@^1.0.0: version "1.2.0" @@ -4406,15 +4464,30 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" - integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-ci@^1.0.10: version "1.2.1" @@ -4525,10 +4598,10 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" @@ -4545,11 +4618,23 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -4622,12 +4707,13 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" -is-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" - integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: - has-symbols "^1.0.1" + call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-regexp@^1.0.0: version "1.0.0" @@ -4639,6 +4725,11 @@ is-retry-allowed@^1.0.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4649,17 +4740,19 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" @@ -4678,6 +4771,13 @@ is-utf8@^0.2.1: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4698,7 +4798,7 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -5145,15 +5245,7 @@ jest-watcher@^26.1.0: jest-util "^26.1.0" string-length "^4.0.1" -jest-worker@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.1.0.tgz#65d5641af74e08ccd561c240e7db61284f82f33d" - integrity sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ== - dependencies: - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^26.2.1: +jest-worker@^26.1.0, jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -5184,6 +5276,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -5315,13 +5414,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" - integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w== +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" + integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== dependencies: - array-includes "^3.1.1" - object.assign "^4.1.0" + array-includes "^3.1.3" + object.assign "^4.1.2" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" @@ -5733,6 +5832,11 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -5788,7 +5892,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.1: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.1: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5882,14 +5986,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" - integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== - dependencies: - semver "^6.0.0" - -make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -6032,23 +6129,23 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" - integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== merge@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== -micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== +micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" - picomatch "^2.0.5" + picomatch "^2.2.3" micromatch@^3.0.4, micromatch@^3.1.4: version "3.1.10" @@ -6081,20 +6178,13 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.34: +mime-types@2.1.34, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== dependencies: mime-db "1.51.0" -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.32" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" - integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== - dependencies: - mime-db "1.49.0" - mime@^2.4.3: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" @@ -6110,10 +6200,10 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@^3.0.4, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" @@ -6130,10 +6220,10 @@ minimist@1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== minimist@~0.0.1: version "0.0.10" @@ -6213,7 +6303,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.0.0, ms@^2.1.1: +ms@2.1.2, ms@^2.0.0, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -6645,12 +6735,12 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -6662,34 +6752,33 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" -object.entries@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" - integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== +object.entries@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" + integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.5" - has "^1.0.3" + es-abstract "^1.19.1" -object.fromentries@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" - integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== +object.fromentries@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" + integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - has "^1.0.3" + es-abstract "^1.19.1" object.getownpropertydescriptors@^2.0.3: version "2.1.0" @@ -6699,6 +6788,14 @@ object.getownpropertydescriptors@^2.0.3: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" +object.hasown@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" + integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.19.1" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -6706,15 +6803,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" - integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - has "^1.0.3" + es-abstract "^1.19.1" octokit-pagination-methods@^1.1.0: version "1.1.0" @@ -7104,7 +7200,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2: +picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -7134,13 +7230,6 @@ pkg-conf@^2.1.0: find-up "^2.0.0" load-json-file "^4.0.0" -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -7206,10 +7295,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" - integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== +prettier@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4" + integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A== pretty-data@^0.40.0: version "0.40.0" @@ -7241,11 +7330,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - promise-inflight@^1.0.1, promise-inflight@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -7274,14 +7358,14 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" object-assign "^4.1.1" - react-is "^16.8.1" + react-is "^16.13.1" proto-list@~1.2.1: version "1.2.4" @@ -7391,7 +7475,7 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-is@^16.12.0, react-is@^16.8.1: +react-is@^16.12.0, react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -7580,18 +7664,18 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" - integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== +regexp.prototype.flags@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" -regexpp@^3.0.0, regexpp@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== registry-auth-token@^3.0.1: version "3.4.0" @@ -7738,16 +7822,7 @@ resolve@1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: - version "1.21.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" - integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== - dependencies: - is-core-module "^2.8.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.19.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -7756,6 +7831,14 @@ resolve@^1.19.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^2.0.0-next.3: + version "2.0.0-next.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" + integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -7797,14 +7880,7 @@ right-pad@^1.0.1: resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" integrity sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA= -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -rimraf@3.0.2, rimraf@^3.0.0: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -7992,7 +8068,7 @@ semver@6.3.0, semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2: +semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -8075,13 +8151,14 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -side-channel@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" - integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: - es-abstract "^1.17.0-next.1" - object-inspect "^1.7.0" + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" @@ -8112,15 +8189,6 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - slide@^1.1.6, slide@~1.1.3, slide@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -8427,33 +8495,35 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.matchall@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" - integrity sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg== +string.prototype.matchall@^4.0.6: + version "4.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0" - has-symbols "^1.0.1" - internal-slot "^1.0.2" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.2" - -string.prototype.trimend@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" - integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.1" + side-channel "^1.0.4" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.5" -string.prototype.trimstart@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" - integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.5" string_decoder@^1.1.1: version "1.3.0" @@ -8509,12 +8579,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-bom@4.0.0, strip-bom@^4.0.0: version "4.0.0" @@ -8546,10 +8616,10 @@ strip-json-comments@3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -strip-json-comments@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strip-json-comments@~2.0.1: version "2.0.1" @@ -8606,16 +8676,6 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - tar@^4.4.10, tar@^4.4.12, tar@^4.4.13: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" @@ -8852,14 +8912,14 @@ ts-node@^8.10.2: source-map-support "^0.5.17" yn "3.1.1" -tsconfig-paths@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" - integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== +tsconfig-paths@^3.12.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" tslib@2.1.0: @@ -8877,10 +8937,10 @@ tslib@^2.2.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -8930,6 +8990,11 @@ type-fest@^0.12.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee" integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -8960,10 +9025,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.9.6: - version "3.9.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" - integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== +typescript@4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== uglify-js@^3.1.4: version "3.13.5" @@ -8980,6 +9045,16 @@ umask@^1.1.0, umask@~1.1.0: resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -9234,6 +9309,17 @@ whatwg-url@^8.0.0: tr46 "^2.0.2" webidl-conversions "^5.0.0" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -9359,13 +9445,6 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@^7.2.3: version "7.3.0" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" From 1e330d09c140773bea1f36c38f81e8cf8085a5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Thu, 24 Mar 2022 01:36:58 +0100 Subject: [PATCH 05/16] refactor(oas): rework all translators --- package.json | 14 +- src/context.ts | 9 + src/guards.ts | 15 + src/merge.ts | 2 +- .../__snapshots__/operation.test.ts.snap | 83 +++++- .../__snapshots__/service.test.ts.snap | 20 -- src/oas/__tests__/accessors.test.ts | 55 ++-- src/oas/__tests__/service.test.ts | 4 +- src/oas/__tests__/tag.test.ts | 17 -- src/oas/__tests__/tags.test.ts | 48 ++++ src/oas/accessors.ts | 68 +++-- src/oas/guards.ts | 35 ++- src/oas/index.ts | 2 +- src/oas/operation.ts | 45 +-- src/oas/service.ts | 52 ++++ src/oas/tag.ts | 5 - src/oas/tags.ts | 45 +++ src/oas/transformers/index.ts | 1 - .../schema/__tests__/schema.spec.ts | 5 +- src/oas/transformers/schema/index.ts | 42 +-- .../transformers/schema/keywords/format.ts | 4 +- src/oas/transformers/security.ts | 0 src/oas/transformers/translateLogo.ts | 8 +- src/oas/types.ts | 15 +- .../__snapshots__/operation.test.ts.snap | 11 +- src/oas2/__tests__/accessors.test.ts | 31 ++- src/oas2/__tests__/operation.test.ts | 16 +- src/oas2/__tests__/service.test.ts | 2 - src/oas2/accessors.ts | 50 ++-- src/oas2/guards.ts | 26 +- src/oas2/operation.ts | 64 ++--- src/oas2/service.ts | 121 ++++---- .../__snapshots__/params.test.ts.snap | 8 + .../__snapshots__/request.test.ts.snap | 43 ++- .../__tests__/getExamplesFromSchema.test.ts | 30 -- .../transformers/__tests__/params.test.ts | 43 ++- .../transformers/__tests__/request.test.ts | 32 +-- .../transformers/__tests__/responses.test.ts | 31 +-- .../transformers/__tests__/securities.test.ts | 7 +- .../transformers/__tests__/servers.test.ts | 9 +- src/oas2/transformers/content.ts | 0 .../transformers/getExamplesFromSchema.ts | 12 - src/oas2/transformers/params.ts | 175 +++++++----- src/oas2/transformers/request.ts | 75 +++-- src/oas2/transformers/responses.ts | 65 ++--- src/oas2/transformers/securities.ts | 174 ++++++------ src/oas2/transformers/servers.ts | 62 +++-- src/oas2/types.ts | 10 + .../__snapshots__/operation.test.ts.snap | 11 +- src/oas3/__tests__/operation.test.ts | 47 +--- src/oas3/__tests__/service.test.ts | 8 - src/oas3/accessors.ts | 27 +- src/oas3/guards.ts | 30 +- src/oas3/operation.ts | 35 ++- src/oas3/service.ts | 106 +++---- .../__snapshots__/content.test.ts.snap | 75 +++-- .../__snapshots__/request.test.ts.snap | 20 +- .../__snapshots__/responses.test.ts.snap | 10 +- .../transformers/__tests__/content.test.ts | 59 +--- .../transformers/__tests__/request.test.ts | 56 ++-- .../transformers/__tests__/responses.test.ts | 9 +- .../transformers/__tests__/securities.test.ts | 8 +- .../transformers/__tests__/servers.test.ts | 107 +++++++- src/oas3/transformers/callbacks.ts | 54 ++-- src/oas3/transformers/content.ts | 259 +++++++++++------- src/oas3/transformers/request.ts | 206 ++++++++------ src/oas3/transformers/responses.ts | 70 ++--- src/oas3/transformers/securities.ts | 41 ++- src/oas3/transformers/servers.ts | 107 ++++++-- src/oas3/types.ts | 10 + .../transformers/__tests__/params.test.ts | 4 +- src/postman/transformers/params.ts | 8 +- src/postman/transformers/securityScheme.ts | 6 +- src/types.ts | 24 +- src/utils.ts | 21 +- yarn.lock | 64 ++++- 76 files changed, 1876 insertions(+), 1297 deletions(-) create mode 100644 src/context.ts create mode 100644 src/guards.ts delete mode 100644 src/oas/__tests__/tag.test.ts create mode 100644 src/oas/__tests__/tags.test.ts create mode 100644 src/oas/service.ts delete mode 100644 src/oas/tag.ts create mode 100644 src/oas/tags.ts create mode 100644 src/oas/transformers/security.ts delete mode 100644 src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts create mode 100644 src/oas2/transformers/content.ts delete mode 100644 src/oas2/transformers/getExamplesFromSchema.ts create mode 100644 src/oas2/types.ts create mode 100644 src/oas3/types.ts diff --git a/package.json b/package.json index 64a29b26..6d059d7e 100644 --- a/package.json +++ b/package.json @@ -53,13 +53,14 @@ }, "dependencies": { "@stoplight/json": "^3.17.2", - "@stoplight/types": "^12.4.0", + "@stoplight/types": "^13.0.0-beta.4", + "@types/json-schema": "7.0.5", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", - "@types/urijs": "~1.19.9", - "json-schema": "^0.4.0", "json-schema-generator": "^2.0.6", - "lodash": "^4.17.15", + "lodash.isequal": "^4.5.0", + "lodash.pick": "^4.4.0", + "lodash.pickby": "^4.6.0", "openapi3-ts": "^2.0.1", "postman-collection": "^4.1.0", "tslib": "^2.3.1", @@ -70,8 +71,9 @@ "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", - "@types/json-schema": "7.0.5", - "@types/lodash": "4.14.157", + "@types/lodash.isequal": "^4.5.5", + "@types/lodash.pick": "^4.4.6", + "@types/lodash.pickby": "^4.6.6", "@types/postman-collection": "^3.5.3", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 00000000..d13668e0 --- /dev/null +++ b/src/context.ts @@ -0,0 +1,9 @@ +import { TransformerContext } from './types'; +import { maybeResolveLocalRef } from './utils'; + +export function createContext>(document: T): TransformerContext { + return { + document, + maybeResolveLocalRef: maybeResolveLocalRef.bind(null, document), + }; +} diff --git a/src/guards.ts b/src/guards.ts new file mode 100644 index 00000000..d57fa038 --- /dev/null +++ b/src/guards.ts @@ -0,0 +1,15 @@ +export function isNonNullable(value: T): value is NonNullable { + return value !== undefined && value !== null; +} + +export function isBoolean(input: unknown): input is boolean { + return typeof input === 'boolean'; +} + +export function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +export function isSerializablePrimitive(value: unknown): value is string | number | boolean | null { + return isBoolean(value) || isString(value) || typeof value === 'number' || value === null; +} diff --git a/src/merge.ts b/src/merge.ts index 35522069..b007adae 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -8,7 +8,7 @@ import { IServer, } from '@stoplight/types'; import type { JSONSchema7 as JSONSchema } from 'json-schema'; -import { isEqual } from 'lodash'; +import isEqual = require('lodash.isequal'); function isExclusivelyAnyOfSchema(schema: JSONSchema): schema is { anyOf: JSONSchema[] } { return !!(schema.anyOf && Object.keys(schema).length === 1); diff --git a/src/oas/__tests__/__snapshots__/operation.test.ts.snap b/src/oas/__tests__/__snapshots__/operation.test.ts.snap index 53a1877f..be3cd93d 100644 --- a/src/oas/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/operation.test.ts.snap @@ -3,6 +3,7 @@ exports[`oas operation openapi v2 1`] = ` Array [ Object { + "deprecated": false, "description": "", "extensions": Object { "x-another": Object { @@ -12,6 +13,7 @@ Array [ }, "id": "?http-operation-id?", "iid": "addPet", + "internal": false, "method": "post", "path": "/pets", "request": Object { @@ -37,6 +39,10 @@ Array [ "description": "Pet object that needs to be added to the store", "required": true, }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], }, "responses": Array [ Object { @@ -66,9 +72,11 @@ Array [ ], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -80,14 +88,19 @@ Array [ ], }, Object { + "deprecated": false, "extensions": Object {}, "id": "?http-operation-id?", + "internal": false, "method": "get", "path": "/pets", "request": Object { + "cookie": Array [], "headers": Array [ Object { + "deprecated": false, "name": "Rate-Limit", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -95,9 +108,12 @@ Array [ "style": "simple", }, ], + "path": Array [], "query": Array [ Object { + "deprecated": false, "name": "skip", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -105,7 +121,9 @@ Array [ "style": "form", }, Object { + "deprecated": false, "name": "limit", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -125,9 +143,11 @@ Array [ "security": Array [], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -135,14 +155,19 @@ Array [ "tags": Array [], }, Object { + "deprecated": false, "extensions": Object {}, "id": "?http-operation-id?", + "internal": false, "method": "options", "path": "/pets", "request": Object { + "cookie": Array [], "headers": Array [ Object { + "deprecated": false, "name": "Rate-Limit", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -150,9 +175,12 @@ Array [ "style": "simple", }, ], + "path": Array [], "query": Array [ Object { + "deprecated": false, "name": "skip", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -160,7 +188,9 @@ Array [ "style": "form", }, Object { + "deprecated": false, "name": "limit", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -176,8 +206,10 @@ Array [ "description": "", "headers": Array [ Object { + "deprecated": false, "description": "Allowed clients", "name": "Allow", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -185,8 +217,10 @@ Array [ "style": "simple", }, Object { + "deprecated": false, "description": "Remaining requests", "name": "X-Rate-Limit", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "int64", @@ -202,9 +236,11 @@ Array [ "security": Array [], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -212,12 +248,19 @@ Array [ "tags": Array [], }, Object { + "deprecated": false, "description": "", "extensions": Object {}, "id": "?http-operation-id?", + "internal": false, "method": "delete", "path": "/pets", - "request": Object {}, + "request": Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], + }, "responses": Array [ Object { "code": "400", @@ -235,9 +278,11 @@ Array [ "security": Array [], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -245,10 +290,12 @@ Array [ "tags": Array [], }, Object { + "deprecated": false, "description": "", "extensions": Object {}, "id": "?http-operation-id?", "iid": "updatePet", + "internal": false, "method": "put", "path": "/pet/{petId}", "request": Object { @@ -274,8 +321,11 @@ Array [ "description": "Pet object that needs to be added to the store", "required": true, }, + "cookie": Array [], + "headers": Array [], "path": Array [ Object { + "deprecated": false, "name": "petId", "required": true, "schema": Object { @@ -285,6 +335,7 @@ Array [ "style": "simple", }, ], + "query": Array [], }, "responses": Array [ Object { @@ -343,9 +394,11 @@ Array [ ], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -516,14 +569,12 @@ Array [ ], "servers": Array [ Object { - "description": undefined, + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", - "variables": undefined, }, Object { - "description": undefined, + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", - "variables": undefined, }, ], "summary": "Add a new pet to the store", @@ -547,20 +598,26 @@ Array [ "path": Array [], "query": Array [ Object { + "deprecated": false, "examples": Array [], + "explode": false, "name": "skip", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, + "style": "simple", }, Object { + "deprecated": false, "examples": Array [], + "explode": false, "name": "limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, + "style": "simple", }, ], }, @@ -586,14 +643,12 @@ Array [ "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", - "variables": undefined, }, Object { - "description": undefined, + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", - "variables": undefined, }, ], "summary": "List pets", @@ -721,13 +776,15 @@ Array [ "headers": Array [], "path": Array [ Object { + "deprecated": false, "examples": Array [], + "explode": false, "name": "petId", - "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, + "style": "simple", }, ], "query": Array [], @@ -792,14 +849,12 @@ Array [ ], "servers": Array [ Object { - "description": undefined, + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", - "variables": undefined, }, Object { - "description": undefined, + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", - "variables": undefined, }, ], "summary": "Update an existing pet", diff --git a/src/oas/__tests__/__snapshots__/service.test.ts.snap b/src/oas/__tests__/__snapshots__/service.test.ts.snap index 792d26c6..62583794 100644 --- a/src/oas/__tests__/__snapshots__/service.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/service.test.ts.snap @@ -37,12 +37,10 @@ Object { ], "servers": Array [ Object { - "description": undefined, "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "description": undefined, "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -50,10 +48,6 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", - "externalDocs": Object { - "description": "Find out more", - "url": "http://swagger.io", - }, "name": "pet", }, Object { @@ -62,10 +56,6 @@ Object { }, Object { "description": "Operations about user", - "externalDocs": Object { - "description": "Find out more about our store", - "url": "http://swagger.io", - }, "name": "user", }, ], @@ -109,12 +99,10 @@ Object { ], "servers": Array [ Object { - "description": undefined, "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "description": undefined, "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -122,10 +110,6 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", - "externalDocs": Object { - "description": "Find out more", - "url": "http://swagger.io", - }, "name": "pet", }, Object { @@ -134,10 +118,6 @@ Object { }, Object { "description": "Operations about user", - "externalDocs": Object { - "description": "Find out more about our store", - "url": "http://swagger.io", - }, "name": "user", }, ], diff --git a/src/oas/__tests__/accessors.test.ts b/src/oas/__tests__/accessors.test.ts index e833c713..29879b23 100644 --- a/src/oas/__tests__/accessors.test.ts +++ b/src/oas/__tests__/accessors.test.ts @@ -1,27 +1,29 @@ -import { getOasTags, getValidOasParameters } from '../accessors'; +import { getValidOasParameters } from '../accessors'; +import { OasVersion } from '../types'; describe('getOasParameters', () => { it('should return empty array', () => { - expect(getValidOasParameters({}, undefined, undefined)).toEqual([]); + expect(getValidOasParameters({}, OasVersion.OAS2, undefined, undefined)).toEqual([]); }); it('should fallback to operation parameters', () => { expect( getValidOasParameters( {}, + OasVersion.OAS2, [ - { name: 'n1', in: 'i1' }, - { name: 'n2', in: 'i2' }, + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, ], undefined, ), ).toEqual([ { - in: 'i1', + in: 'header', name: 'n1', }, { - in: 'i2', + in: 'query', name: 'n2', }, ]); @@ -29,17 +31,17 @@ describe('getOasParameters', () => { it('should fallback to path parameters', () => { expect( - getValidOasParameters({}, undefined, [ - { name: 'n1', in: 'i1' }, - { name: 'n2', in: 'i2' }, + getValidOasParameters({}, OasVersion.OAS2, undefined, [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, ]), ).toEqual([ { - in: 'i1', + in: 'header', name: 'n1', }, { - in: 'i2', + in: 'query', name: 'n2', }, ]); @@ -49,45 +51,30 @@ describe('getOasParameters', () => { expect( getValidOasParameters( {}, + OasVersion.OAS3, [ - { name: 'n1', in: 'n1', type: 'array' }, - { name: 'no2', in: 'io2' }, + { name: 'n1', in: 'query', type: 'array' }, + { name: 'no2', in: 'header' }, ], [ - { name: 'n1', in: 'n1', type: 'string' }, - { name: 'np3', in: 'ip3' }, + { name: 'n1', in: 'query', type: 'string' }, + { name: 'np3', in: 'header' }, ], ), ).toEqual([ { - in: 'n1', + in: 'query', name: 'n1', type: 'array', }, { - in: 'io2', + in: 'header', name: 'no2', }, { - in: 'ip3', + in: 'header', name: 'np3', }, ]); }); }); - -describe('getOasTags', () => { - describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { - it('should return empty array', () => { - expect(getOasTags(tags)).toEqual([]); - }); - }); - - it('should filter out invalid values', () => { - expect(getOasTags([{}, null, 'foo'])).toEqual(['foo']); - }); - - it('should normalize values', () => { - expect(getOasTags([0, 'foo', true])).toEqual(['0', 'foo', 'true']); - }); -}); diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 6a8d8f8a..2a0b0884 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -1,7 +1,7 @@ import { transformOas2Service } from '../../oas2/service'; import { transformOas3Service } from '../../oas3/service'; -import * as oas2KitchenSinkJson from './fixtures//oas2-kitchen-sink.json'; -import * as oas3KitchenSinkJson from './fixtures//oas3-kitchen-sink.json'; +import * as oas2KitchenSinkJson from './fixtures/oas2-kitchen-sink.json'; +import * as oas3KitchenSinkJson from './fixtures/oas3-kitchen-sink.json'; describe('oas service', () => { it('openapi v2', () => { diff --git a/src/oas/__tests__/tag.test.ts b/src/oas/__tests__/tag.test.ts deleted file mode 100644 index 381e5b43..00000000 --- a/src/oas/__tests__/tag.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { translateToTags } from '../tag'; - -describe('translateToTags', () => { - it('should translate array of strings to tags', () => { - expect(translateToTags(['a', 'b', 'c'])).toEqual([ - { - name: 'a', - }, - { - name: 'b', - }, - { - name: 'c', - }, - ]); - }); -}); diff --git a/src/oas/__tests__/tags.test.ts b/src/oas/__tests__/tags.test.ts new file mode 100644 index 00000000..9efcbaa5 --- /dev/null +++ b/src/oas/__tests__/tags.test.ts @@ -0,0 +1,48 @@ +import { createContext } from '../../context'; +import { translateToTags as _translateToTags } from '../tags'; + +const translateToTags = (tags: unknown) => _translateToTags.call(createContext({}), tags); + +describe('translateToTags', () => { + describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { + it('should return empty array', () => { + expect(translateToTags(tags)).toStrictEqual([]); + }); + }); + + it('should filter out invalid values', () => { + expect(translateToTags([{}, null, 'foo'])).toStrictEqual([ + { + name: 'foo', + }, + ]); + }); + + it('should normalize values', () => { + expect(translateToTags([0, 'foo', true])).toStrictEqual([ + { + name: '0', + }, + { + name: 'foo', + }, + { + name: 'true', + }, + ]); + }); + + it('should translate array of strings to tags', () => { + expect(translateToTags(['a', 'b', 'c'])).toStrictEqual([ + { + name: 'a', + }, + { + name: 'b', + }, + { + name: 'c', + }, + ]); + }); +}); diff --git a/src/oas/accessors.ts b/src/oas/accessors.ts index 5ab865d9..50675aad 100644 --- a/src/oas/accessors.ts +++ b/src/oas/accessors.ts @@ -1,34 +1,52 @@ -import { Extensions } from '@stoplight/types'; -import { fromPairs, isObject, map, unionBy } from 'lodash'; +import type { Extensions } from '@stoplight/types'; -import { maybeResolveLocalRef } from '../utils'; - -type ParamTypeBase = { name: string; in: string }; +import { Fragment } from '../types'; +import { entries, maybeResolveLocalRef } from '../utils'; +import { isValidOas2Param, isValidOas3Param, Oas2ParamBase, Oas3ParamBase, ParamBase } from './guards'; +import { OasVersion } from './types'; const ROOT_EXTENSIONS = ['x-internal']; -export function getValidOasParameters( - document: unknown, - operationParameters: ParamType[] | undefined, - pathParameters: ParamType[] | undefined, -) { - const resolvedOperationParams = map(operationParameters, x => maybeResolveLocalRef(document, x) as ParamType); - const resolvedPathParams = map(pathParameters, x => maybeResolveLocalRef(document, x) as ParamType); - - return unionBy(resolvedOperationParams, resolvedPathParams, (parameter?: ParamType) => { - return isObject(parameter) ? `${parameter.name}-${parameter.in}` : 'invalid'; - }) - .filter(isObject) - .filter(isValidOasParameter); -} +function getParameters(document: Fragment, spec: OasVersion.OAS2, params: unknown): Oas2ParamBase[]; +function getParameters(document: Fragment, spec: OasVersion.OAS3, params: unknown): Oas3ParamBase[]; +function getParameters(document: Fragment, spec: OasVersion, params: unknown): ParamBase[] { + if (!Array.isArray(params)) return []; -const isValidOasParameter = (parameter: Partial): parameter is ParamTypeBase => - 'name' in parameter && typeof parameter.name === 'string' && 'in' in parameter && typeof parameter.in === 'string'; + const resolved = params.map(maybeResolveLocalRef.bind(null, document)); + return spec === OasVersion.OAS2 ? resolved.filter(isValidOas2Param) : resolved.filter(isValidOas3Param); +} -export function getOasTags(tags: unknown): string[] { - return Array.isArray(tags) ? tags.filter(tag => typeof tag !== 'object').map(String) : []; +const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; + +export function getValidOasParameters( + document: Fragment, + spec: OasVersion.OAS2, + operationParams: unknown, + pathParams: unknown, +): Oas2ParamBase[]; +export function getValidOasParameters( + document: Fragment, + spec: OasVersion.OAS3, + operationParams: unknown, + pathParams: unknown, +): Oas3ParamBase[]; +export function getValidOasParameters( + document: Fragment, + spec: any, + operationParams: unknown, + pathParams: unknown, +): ParamBase[] { + const uniqueParameters: Record = {}; + + const params = [...getParameters(document, spec, operationParams), ...getParameters(document, spec, pathParams)]; + + for (const param of params) { + uniqueParameters[getIdForParameter(param)] ??= param; + } + + return Object.values(uniqueParameters); } -export function getExtensions(target: Record): Extensions { - return fromPairs(Object.entries(target).filter(([key]) => key.startsWith('x-') && !ROOT_EXTENSIONS.includes(key))); +export function getExtensions(target: unknown): Extensions { + return Object.fromEntries(entries(target).filter(([key]) => key.startsWith('x-') && !ROOT_EXTENSIONS.includes(key))); } diff --git a/src/oas/guards.ts b/src/oas/guards.ts index 226c5769..e0605918 100644 --- a/src/oas/guards.ts +++ b/src/oas/guards.ts @@ -1,11 +1,34 @@ -import { DeepPartial, Dictionary } from '@stoplight/types'; -import { InfoObject } from 'openapi3-ts/src/model/OpenApi'; -import { Info } from 'swagger-schema-official'; - -import { isDictionary } from '../utils'; +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial, Dictionary } from '@stoplight/types'; +import { HttpParamStyles } from '@stoplight/types'; +import { ParameterLocation } from 'openapi3-ts'; +import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; +import type { Info } from 'swagger-schema-official'; +import { BaseParameter } from 'swagger-schema-official'; export function hasXLogo( info: DeepPartial, ): info is DeepPartial & { 'x-logo': Dictionary } { - return isDictionary(info['x-logo']); + return isPlainObject(info['x-logo']); } + +const VALID_OAS3_PARAM_LOCATION: ParameterLocation[] = ['query', 'header', 'path', 'cookie']; +const VALID_OAS2_PARAM_LOCATION: BaseParameter['in'][] = ['query', 'header', 'path', 'body', 'formData']; + +const VALID_PARAM_STYLES: HttpParamStyles[] = Object.values(HttpParamStyles); + +export type Oas3ParamBase = { name: string; in: ParameterLocation }; +export type Oas2ParamBase = { name: string; in: BaseParameter['in'] }; +export type ParamBase = { name: string; in: string }; + +export const isValidOasParam = (param: unknown): param is ParamBase => + isPlainObject(param) && typeof param.name === 'string' && typeof param.in === 'string'; + +export const isValidOas2Param = (param: unknown): param is Oas2ParamBase => + isValidOasParam(param) && VALID_OAS2_PARAM_LOCATION.includes(param.in as BaseParameter['in']); + +export const isValidOas3Param = (param: unknown): param is Oas3ParamBase => + isValidOasParam(param) && VALID_OAS3_PARAM_LOCATION.includes(param.in as ParameterLocation); + +export const isValidParamStyle = (style: unknown): style is HttpParamStyles => + VALID_PARAM_STYLES.includes(style as HttpParamStyles); diff --git a/src/oas/index.ts b/src/oas/index.ts index ca4dfd22..633a1ce4 100644 --- a/src/oas/index.ts +++ b/src/oas/index.ts @@ -1,2 +1,2 @@ export * from './operation'; -export { translateSchemaObject } from './transformers/index'; +export * from './types'; diff --git a/src/oas/operation.ts b/src/oas/operation.ts index f4601bf2..94bb6864 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,33 +1,34 @@ -import { IHttpOperation } from '@stoplight/types'; -import { flatten, get, keys, map } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial, IHttpOperation } from '@stoplight/types'; +import type { OpenAPIObject } from 'openapi3-ts'; +import type { Spec } from 'swagger-schema-official'; -import { HttpOperationTransformer } from '../types'; +import type { HttpOperationTransformer } from '../types'; const DEFAULT_METHODS = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch', 'trace']; export function transformOasOperations( - document: Spec | OpenAPIObject, + document: DeepPartial, transformer: HttpOperationTransformer, methods: string[] | null = DEFAULT_METHODS, ): IHttpOperation[] { - const paths = keys(get(document, 'paths')); + const paths = isPlainObject(document.paths) ? Object.keys(document.paths) : []; - return flatten( - map(paths, path => { - let operations = keys(get(document, ['paths', path])); - if (methods !== null) { - operations = operations.filter(pathKey => methods.includes(pathKey)); - } + return paths.flatMap(path => { + const value = document.paths![path]; + if (!isPlainObject(value)) return []; - return operations.map(method => - transformer({ - document, - path, - method, - }), - ); - }), - ); + let operations = Object.keys(value); + if (methods !== null) { + operations = operations.filter(pathKey => methods.includes(pathKey)); + } + + return operations.map(method => + transformer({ + document, + path, + method, + }), + ); + }); } diff --git a/src/oas/service.ts b/src/oas/service.ts new file mode 100644 index 00000000..c6cb6743 --- /dev/null +++ b/src/oas/service.ts @@ -0,0 +1,52 @@ +import { isPlainObject } from '@stoplight/json'; +import { DeepPartial, IHttpService } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; + +import { isNonNullable, isString } from '../guards'; +import { TranslateFunction } from '../types'; +import { hasXLogo } from './guards'; +import { translateTagDefinition } from './tags'; +import { translateLogo } from './transformers/translateLogo'; + +export const transformOasService: TranslateFunction | DeepPartial, [], IHttpService> = + function () { + const document = this.document; + + const httpService: IHttpService = { + id: '?http-service-id?', + + version: document.info?.version ?? '', + name: document.info?.title ?? 'no-title', + + ...pickBy( + { + description: document.info?.description, + termsOfService: document.info?.termsOfService, + }, + isString, + ), + + ...pickBy( + { + contact: document.info?.contact, + }, + isPlainObject, + ), + }; + + if (isPlainObject(document.info) && hasXLogo(document.info)) { + httpService.logo = translateLogo(document.info); + } + + const tags = Array.isArray(document.tags) + ? document.tags.map(translateTagDefinition, this).filter(isNonNullable) + : []; + + if (tags.length > 0) { + httpService.tags = tags; + } + + return httpService; + }; diff --git a/src/oas/tag.ts b/src/oas/tag.ts deleted file mode 100644 index 1d9fd9c2..00000000 --- a/src/oas/tag.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { INodeTag } from '@stoplight/types'; - -export function translateToTags(tags: string[]): INodeTag[] { - return tags.map(tag => ({ name: tag })); -} diff --git a/src/oas/tags.ts b/src/oas/tags.ts new file mode 100644 index 00000000..7f1ec25d --- /dev/null +++ b/src/oas/tags.ts @@ -0,0 +1,45 @@ +import { isPlainObject } from '@stoplight/json'; +import type { INodeTag, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); + +import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; +import type { ArrayCallbackParameters, Fragment, TranslateFunction } from '../types'; + +export const translateTag: TranslateFunction< + Fragment, + ArrayCallbackParameters, + Optional +> = function (tag) { + if (tag === null || !isSerializablePrimitive(tag)) return; + + return { + name: String(tag), + }; +}; + +export const translateTagDefinition: TranslateFunction< + Fragment, + ArrayCallbackParameters, + Optional +> = function (tag, ...params) { + if (!isPlainObject(tag)) return; + + const translatedTag = translateTag.call(this, tag.name, ...params); + + if (!translatedTag) return; + + return { + ...translatedTag, + + ...pickBy( + { + description: tag.description, + }, + isString, + ), + }; +}; + +export const translateToTags: TranslateFunction = function (tags) { + return Array.isArray(tags) ? tags.map(translateTag, this).filter(isNonNullable) : []; +}; diff --git a/src/oas/transformers/index.ts b/src/oas/transformers/index.ts index d0152846..452614f3 100644 --- a/src/oas/transformers/index.ts +++ b/src/oas/transformers/index.ts @@ -1,2 +1 @@ export { translateSchemaObject } from './schema/index'; -export { translateLogo } from './translateLogo'; diff --git a/src/oas/transformers/schema/__tests__/schema.spec.ts b/src/oas/transformers/schema/__tests__/schema.spec.ts index 02618bcd..bea202ba 100644 --- a/src/oas/transformers/schema/__tests__/schema.spec.ts +++ b/src/oas/transformers/schema/__tests__/schema.spec.ts @@ -1,7 +1,8 @@ +import { createContext } from '../../../../context'; import { translateSchemaObject } from '..'; -import { OASSchemaObject } from '../types'; +import type { OASSchemaObject } from '../types'; -const translate = (schemaObject: OASSchemaObject) => translateSchemaObject({}, schemaObject); +const translate = (schemaObject: OASSchemaObject) => translateSchemaObject.call(createContext({}), schemaObject); describe('translateSchemaObject', () => { it('should translate id', () => { diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index 225424c2..cd12e29c 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -1,11 +1,12 @@ -import { DeepPartial } from '@stoplight/types'; +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial } from '@stoplight/types'; import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; -import { isObject } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import type { OpenAPIObject } from 'openapi3-ts'; +import type { Spec } from 'swagger-schema-official'; +import type { TranslateFunction } from '../../../types'; import keywords from './keywords'; -import { OASSchemaObject } from './types'; +import type { OASSchemaObject } from './types'; const keywordsKeys = Object.keys(keywords); @@ -15,10 +16,13 @@ type InternalOptions = { // Convert from OpenAPI 2.0, OpenAPI 3.0 `SchemaObject` or JSON Schema Draft4/6 to JSON Schema Draft 7 // This converter shouldn't make any differences to Schema objects defined in OpenAPI 3.1, excepts when jsonSchemaDialect is provided. -export function translateSchemaObject( - document: DeepPartial, - schema: OASSchemaObject, -): JSONSchema7 { +export const translateSchemaObject: TranslateFunction< + DeepPartial, + [schema: OASSchemaObject], + JSONSchema7 +> = function (schema) { + const document = this.document; + if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { return { $schema: document.jsonSchemaDialect, @@ -28,7 +32,11 @@ export function translateSchemaObject( }; } - const clonedSchema = convertSchema(schema, { + return convertSchema(schema); +}; + +export function convertSchema(schema: OASSchemaObject) { + const clonedSchema = _convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); @@ -36,7 +44,7 @@ export function translateSchemaObject( return clonedSchema as JSONSchema7; } -function convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONSchema7 { +function _convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONSchema7 { const clonedSchema: OASSchemaObject | JSONSchema7 = { ...schema }; for (const struct of options.structs) { @@ -44,19 +52,19 @@ function convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONS clonedSchema[struct] = clonedSchema[struct].slice(); for (let i = 0; i < clonedSchema[struct].length; i++) { - if (isObject(clonedSchema[struct][i])) { - clonedSchema[struct][i] = convertSchema(clonedSchema[struct][i], options); + if (typeof clonedSchema[struct][i] === 'object' && clonedSchema[struct][i] !== null) { + clonedSchema[struct][i] = _convertSchema(clonedSchema[struct][i], options); } else { clonedSchema[struct].splice(i, 1); i--; } } } else if (clonedSchema[struct] !== null && typeof clonedSchema[struct] === 'object') { - clonedSchema[struct] = convertSchema(clonedSchema[struct], options); + clonedSchema[struct] = _convertSchema(clonedSchema[struct], options); } } - if ('properties' in clonedSchema && isObject(clonedSchema.properties)) { + if ('properties' in clonedSchema && isPlainObject(clonedSchema.properties)) { convertProperties(clonedSchema, options); } @@ -76,8 +84,8 @@ function convertProperties(schema: OASSchemaObject, options: InternalOptions): v for (const key of Object.keys(props)) { const property = props[key]; - if (isObject(property)) { - props[key] = convertSchema(property, options); + if (isPlainObject(property)) { + props[key] = _convertSchema(property, options); } } } diff --git a/src/oas/transformers/schema/keywords/format.ts b/src/oas/transformers/schema/keywords/format.ts index 9f558e9e..3ea30f56 100644 --- a/src/oas/transformers/schema/keywords/format.ts +++ b/src/oas/transformers/schema/keywords/format.ts @@ -1,6 +1,6 @@ -import { JSONSchema7 } from 'json-schema'; +import type { JSONSchema7 } from 'json-schema'; -import { Converter } from '../types'; +import type { Converter } from '../types'; const ranges = { MIN_INT_32: 0 - 2 ** 31, diff --git a/src/oas/transformers/security.ts b/src/oas/transformers/security.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/oas/transformers/translateLogo.ts b/src/oas/transformers/translateLogo.ts index a7ae8301..2e5da953 100644 --- a/src/oas/transformers/translateLogo.ts +++ b/src/oas/transformers/translateLogo.ts @@ -1,7 +1,7 @@ -import { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; -import { pickBy } from 'lodash'; -import { InfoObject } from 'openapi3-ts/src/model/OpenApi'; -import { Info } from 'swagger-schema-official'; +import type { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; +import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; +import type { Info } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); export function translateLogo({ 'x-logo': logo, diff --git a/src/oas/types.ts b/src/oas/types.ts index 16c7bcb0..884fa22d 100644 --- a/src/oas/types.ts +++ b/src/oas/types.ts @@ -1,7 +1,8 @@ -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import { DeepPartial } from '@stoplight/types'; +import type { OpenAPIObject } from 'openapi3-ts'; +import type { Spec } from 'swagger-schema-official'; -import { +import type { HttpOperationTransformer, HttpServiceTransformer, ITransformOperationOpts, @@ -16,15 +17,15 @@ export enum OasVersion { /** * Service */ -export type Oas2TransformServiceOpts = ITransformServiceOpts; -export type Oas3TransformServiceOpts = ITransformServiceOpts; +export type Oas2TransformServiceOpts = ITransformServiceOpts>; +export type Oas3TransformServiceOpts = ITransformServiceOpts>; export type Oas2HttpServiceTransformer = HttpServiceTransformer; export type Oas3HttpServiceTransformer = HttpServiceTransformer; /** * Operation */ -export type Oas2TransformOperationOpts = ITransformOperationOpts; -export type Oas3TransformOperationOpts = ITransformOperationOpts; +export type Oas2TransformOperationOpts = ITransformOperationOpts>; +export type Oas3TransformOperationOpts = ITransformOperationOpts>; export type Oas2HttpOperationTransformer = HttpOperationTransformer; export type Oas3HttpOperationTransformer = HttpOperationTransformer; diff --git a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap index 9215c3f7..d58bd9bb 100644 --- a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap @@ -7,9 +7,15 @@ Object { "extensions": Object {}, "id": "?http-operation-id?", "iid": "oid", + "internal": false, "method": "get", "path": "/users/{userId}", - "request": Object {}, + "request": Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], + }, "responses": Array [ Object { "code": "response", @@ -28,8 +34,9 @@ Object { "description": "desc", "headers": Array [ Object { - "description": undefined, + "deprecated": false, "name": "header", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "integer", diff --git a/src/oas2/__tests__/accessors.test.ts b/src/oas2/__tests__/accessors.test.ts index bd8b6965..08e2b16b 100644 --- a/src/oas2/__tests__/accessors.test.ts +++ b/src/oas2/__tests__/accessors.test.ts @@ -1,7 +1,7 @@ import { Dictionary } from '@stoplight/types'; import { Security } from 'swagger-schema-official'; -import { getConsumes, getProduces, getSecurities } from '../accessors'; +import { getConsumes, getExamplesFromSchema, getProduces, getSecurities } from '../accessors'; const securityDefinitionsFixture: Dictionary = { api_key: { @@ -268,4 +268,33 @@ describe('accessors', () => { expect(getConsumes({ consumes: ['text/plain', null] } as any, {})).toEqual(['text/plain']); }); }); + + describe('getExamplesFromSchema', () => { + it('should ignore invalid data', () => { + // @ts-ignore + expect(getExamplesFromSchema(null)).toEqual({}); + }); + + it('should work with x-examples', () => { + expect( + getExamplesFromSchema({ + 'x-examples': { + 'my-example': {}, + }, + }), + ).toEqual({ + 'my-example': {}, + }); + }); + + it('should work with example', () => { + expect( + getExamplesFromSchema({ + example: 'my-example', + }), + ).toEqual({ + default: 'my-example', + }); + }); + }); }); diff --git a/src/oas2/__tests__/operation.test.ts b/src/oas2/__tests__/operation.test.ts index 25f64672..7e010fef 100644 --- a/src/oas2/__tests__/operation.test.ts +++ b/src/oas2/__tests__/operation.test.ts @@ -156,9 +156,16 @@ describe('transformOas2Operation', () => { }), { id: '?http-operation-id?', + deprecated: false, + internal: false, path: '/users/{userId}', method: 'put', - request: {}, + request: { + cookie: [], + headers: [], + path: [], + query: [], + }, responses: [], security: [], servers: [], @@ -194,18 +201,25 @@ describe('transformOas2Operation', () => { }), ).toStrictEqual({ id: '?http-operation-id?', + deprecated: false, + internal: false, method: 'get', path: '/users/{userId}', request: { + cookie: [], headers: [ { name: 'name', + required: false, + deprecated: false, schema: { $schema: 'http://json-schema.org/draft-07/schema#', }, style: 'simple', }, ], + path: [], + query: [], }, responses: [], security: [], diff --git a/src/oas2/__tests__/service.test.ts b/src/oas2/__tests__/service.test.ts index 34e41e9e..754cd2f5 100644 --- a/src/oas2/__tests__/service.test.ts +++ b/src/oas2/__tests__/service.test.ts @@ -41,12 +41,10 @@ describe('oas2 service', () => { version: '1.0', servers: [ { - description: void 0, name: '', url: 'https://petstore.swagger.io/v2', }, { - description: void 0, name: '', url: 'http://petstore.swagger.io/v2', }, diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index e89d0523..bbcfe522 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,22 +1,20 @@ +import { isPlainObject } from '@stoplight/json'; import { DeepPartial, Dictionary } from '@stoplight/types'; -import { compact, get, isEmpty, isString, keys, map, merge, pickBy } from 'lodash'; -import { negate } from 'lodash/fp'; -import { Operation, Security, Spec } from 'swagger-schema-official'; +import { Operation, Schema, Security, Spec } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); +import { isNonNullable, isString } from '../guards'; import { isSecurityScheme } from './guards'; export type SecurityWithKey = Security & { key: string }; -export function getSecurities( - spec: DeepPartial, - operationSecurity: Dictionary[] | undefined, -): SecurityWithKey[][] { +export function getSecurities(spec: DeepPartial, operationSecurity: unknown): SecurityWithKey[][] { const globalSecurities = getSecurity(spec.security, spec.securityDefinitions || {}); const operationSecurities = getSecurity(operationSecurity, spec.securityDefinitions || {}); const securities = !!operationSecurity ? operationSecurities : globalSecurities; - return securities.filter(negate(isEmpty)); + return securities.filter(a => a.length); } export function getProduces(spec: DeepPartial, operation: DeepPartial) { @@ -27,22 +25,21 @@ export function getConsumes(spec: DeepPartial, operation: DeepPartial, - definitions: DeepPartial, -): SecurityWithKey[][] { - if (!security || !definitions) { +function getSecurity(security: unknown, definitions: DeepPartial): SecurityWithKey[][] { + if (!Array.isArray(security) || !definitions) { return []; } - return map(security, sec => { - return compact( - keys(sec).map(key => { + return security.map(sec => { + if (!isPlainObject(sec)) return []; + return Object.keys(sec) + .map(key => { const def = definitions[key]; if (isSecurityScheme(def)) { - const defCopy = merge<{ key: string }, Security>({ key }, def); - const scopes = sec[key] || []; + const defCopy = { ...def, key }; + const secKey = sec[key]; + const scopes = Array.isArray(secKey) ? secKey : []; // Filter definition scopes by operation scopes if (defCopy.type === 'oauth2' && scopes.length) { @@ -53,8 +50,8 @@ function getSecurity( } return null; - }), - ); + }) + .filter(isNonNullable); }); } @@ -63,10 +60,19 @@ function getProducesOrConsumes( spec: DeepPartial, operation: DeepPartial, ): string[] { - const mimeTypes = get(operation, which, get(spec, which, [])); + const mimeTypes = operation?.[which] || spec?.[which] || []; if (!Array.isArray(mimeTypes)) { return []; } - return compact(mimeTypes).filter(v => v && isString(v)); + return mimeTypes.flat().filter(isString); +} + +export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Record { + if (!isPlainObject(data)) return {}; + + return { + ...('x-examples' in data && isPlainObject(data['x-examples']) && { ...data['x-examples'] }), + ...('example' in data && { default: data.example }), + }; } diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index 7708867e..bf5d124f 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -1,22 +1,18 @@ -import { isObject } from 'lodash'; +import { isPlainObject } from '@stoplight/json'; import type { Response, Security, Tag } from 'swagger-schema-official'; -import { isDictionary } from '../utils'; +import { isValidOas2Param, Oas2ParamBase } from '../oas/guards'; export function isSecurityScheme(maybeSecurityScheme: unknown): maybeSecurityScheme is Security { - return isDictionary(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; + return isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; } export const isTagObject = (maybeTagObject: unknown): maybeTagObject is Tag => { - if (isObject(maybeTagObject) && 'name' in maybeTagObject) { - return typeof (maybeTagObject as Tag).name === 'string'; - } - - return false; + return isPlainObject(maybeTagObject) && typeof maybeTagObject.name === 'string'; }; export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObject is Response => - isObject(maybeResponseObject) && + isPlainObject(maybeResponseObject) && ('description' in maybeResponseObject || 'schema' in maybeResponseObject || 'headers' in maybeResponseObject || @@ -25,3 +21,15 @@ export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObj export function isValidScheme(scheme: unknown): scheme is 'http' | 'https' | 'ws' | 'wss' { return typeof scheme === 'string' && ['http', 'https', 'ws', 'wss'].includes(scheme); } + +export function isQueryParam(param: unknown): param is Oas2ParamBase & { in: 'query' } { + return isValidOas2Param(param) && param.in === 'query'; +} + +export function isPathParam(param: unknown): param is Oas2ParamBase & { in: 'path' } { + return isValidOas2Param(param) && param.in === 'path'; +} + +export function isHeaderParam(param: unknown): param is Oas2ParamBase & { in: 'header' } { + return isValidOas2Param(param) && param.in === 'header'; +} diff --git a/src/oas2/operation.ts b/src/oas2/operation.ts index bece36aa..30e022a3 100644 --- a/src/oas2/operation.ts +++ b/src/oas2/operation.ts @@ -1,13 +1,15 @@ -import { IHttpOperation } from '@stoplight/types'; -import { get, isNil, omitBy } from 'lodash'; -import { Operation, Parameter, Path, Response, Spec } from 'swagger-schema-official'; +import { isPlainObject } from '@stoplight/json'; +import type { IHttpOperation } from '@stoplight/types'; +import type { Spec } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); -import { getExtensions, getOasTags, getValidOasParameters } from '../oas/accessors'; +import { createContext } from '../context'; +import { isString } from '../guards'; +import { getExtensions } from '../oas/accessors'; import { transformOasOperations } from '../oas/operation'; -import { translateToTags } from '../oas/tag'; +import { translateToTags } from '../oas/tags'; import { Oas2HttpOperationTransformer } from '../oas/types'; import { maybeResolveLocalRef } from '../utils'; -import { getConsumes, getProduces } from './accessors'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; @@ -18,42 +20,40 @@ export function transformOas2Operations(document: Spec): IHttpOperation[] { } export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, get(document, ['paths', path])) as Path; - if (!pathObj) { + const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]); + if (!isPlainObject(pathObj)) { throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); } - const operation = maybeResolveLocalRef(document, get(document, ['paths', path, method])) as Operation & { - [extension: string]: unknown; - }; - if (!operation) { + const operation = maybeResolveLocalRef(document, pathObj[method]); + if (!isPlainObject(operation)) { throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); } - const produces = getProduces(document, operation); - const consumes = getConsumes(document, operation); + const ctx = createContext(document); - const httpOperation: IHttpOperation = { - // TODO(SL-248): what shall we do with id? + return { id: '?http-operation-id?', - iid: operation.operationId, - description: operation.description, - deprecated: operation.deprecated, - internal: operation['x-internal'] as boolean, method, path, - summary: operation.summary, - responses: translateToResponses(document, operation.responses as { [name: string]: Response }, produces), - servers: translateToServers(document, operation), - request: translateToRequest( - document, - getValidOasParameters(document, operation.parameters as Parameter[], pathObj.parameters as Parameter[]), - consumes, - ), - tags: translateToTags(getOasTags(operation.tags)), - security: translateToSecurities(document, operation.security), + + deprecated: !!operation.deprecated, + internal: !!operation['x-internal'], + + responses: translateToResponses.call(ctx, operation), + servers: translateToServers.call(ctx, operation), + request: translateToRequest.call(ctx, pathObj, operation), + tags: translateToTags.call(ctx, operation.tags), + security: translateToSecurities.call(ctx, operation.security), extensions: getExtensions(operation), - }; - return omitBy(httpOperation, isNil) as IHttpOperation; + ...pickBy( + { + iid: operation.operationId, + description: operation.description, + summary: operation.summary, + }, + isString, + ), + }; }; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index d8eb9f6c..a67f48d6 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,27 +1,17 @@ -import { HttpSecurityScheme, IHttpService, IServer } from '@stoplight/types'; -import { compact, filter, flatMap, isString, keys, pickBy } from 'lodash'; +import { isPlainObject } from '@stoplight/json'; +import pickBy = require('lodash.pickby'); -import { hasXLogo } from '../oas/guards'; -import { translateLogo } from '../oas/transformers/translateLogo'; +import { createContext } from '../context'; +import { isNonNullable, isString } from '../guards'; +import { transformOasService } from '../oas/service'; import { Oas2HttpServiceTransformer } from '../oas/types'; -import { isDictionary } from '../utils'; -import { isTagObject } from './guards'; +import { entries } from '../utils'; import { translateToSingleSecurity } from './transformers/securities'; +import { translateToServer } from './transformers/servers'; export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) => { - const httpService: IHttpService = { - id: '?http-service-id?', - version: document.info?.version ?? '', - name: document.info?.title ?? 'no-title', - }; - - if (document.info?.description) { - httpService.description = document.info.description; - } - - if (document.info?.contact) { - httpService.contact = document.info.contact; - } + const ctx = createContext(document); + const httpService = transformOasService.call(ctx); if (document.info?.license) { httpService.license = { @@ -30,75 +20,60 @@ export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) = }; } - if (document.info?.termsOfService) { - httpService.termsOfService = document.info.termsOfService; - } + const schemes = Array.isArray(document.schemes) ? document.schemes.filter(isString) : []; - if (isDictionary(document.info) && hasXLogo(document.info)) { - httpService.logo = translateLogo(document.info); - } + const servers = schemes.map(translateToServer, ctx).filter(isNonNullable); - const schemes = filter(document.schemes, scheme => scheme && isString(scheme)); - const servers = schemes.map(scheme => ({ - name: document.info?.title ?? '', - description: undefined, - url: scheme + '://' + (document.host || '') + (document.basePath || ''), - })); if (servers.length) { httpService.servers = servers; } - const securitySchemes = compact( - keys(document.securityDefinitions).map(key => { - const definition = document?.securityDefinitions?.[key]; - if (!definition) return undefined; + const securitySchemes = entries(document.securityDefinitions) + .map(([key, definition]) => { + return isPlainObject(definition) ? translateToSingleSecurity.call(ctx, { ...definition, key }) : null; + }) + .filter(isNonNullable); - return translateToSingleSecurity(definition, key); - }), - ); if (securitySchemes.length) { httpService.securitySchemes = securitySchemes; } - const security = compact( - flatMap(document.security, sec => { - if (!sec) return null; - - return keys(sec).map(key => { - const ss = securitySchemes.find(securityScheme => securityScheme.key === key); - if (ss && ss.type === 'oauth2') { - const flows = {}; - for (const flowKey in ss.flows) { - const flow = ss.flows[flowKey]; - flows[flowKey] = { - ...flow, - scopes: pickBy(flow.scopes, (_val: string, scopeKey: string) => { - const secKey = sec[key]; - if (secKey) return secKey.includes(scopeKey); - return undefined; - }), - }; - } - - return { - ...ss, - flows, - }; - } - - return ss; - }); - }), - ); + const security = Array.isArray(document.security) + ? document.security + .flatMap(sec => { + if (!isPlainObject(sec)) return null; + + return Object.keys(sec).map(key => { + const ss = securitySchemes.find(securityScheme => securityScheme.key === key); + if (ss && ss.type === 'oauth2') { + const flows = {}; + for (const flowKey in ss.flows) { + const flow = ss.flows[flowKey]; + flows[flowKey] = { + ...flow, + scopes: pickBy(flow.scopes, (_val: string, scopeKey: string) => { + const secKey = sec[key]; + if (secKey) return secKey.includes(scopeKey); + return undefined; + }), + }; + } + + return { + ...ss, + flows, + }; + } + + return ss; + }); + }) + .filter(isNonNullable) + : []; if (security.length) { httpService.security = security; } - const tags = filter(document.tags, isTagObject); - if (tags.length) { - httpService.tags = tags; - } - return httpService; }; diff --git a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap index 0fe08866..5c25ea5f 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap @@ -49,6 +49,7 @@ Object { exports[`params.translator translateToHeaderParam should translate header param 1`] = ` Object { + "deprecated": false, "description": "desc", "name": "name", "required": true, @@ -66,8 +67,10 @@ exports[`params.translator translateToHeaderParams should translate empty dictio exports[`params.translator translateToHeaderParams should translate to multiple header params 1`] = ` Array [ Object { + "deprecated": false, "description": "a description", "name": "header-name", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -75,8 +78,10 @@ Array [ "style": "simple", }, Object { + "deprecated": false, "description": "another description", "name": "plain-tex", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -89,8 +94,10 @@ Array [ exports[`params.translator translateToHeaderParams should translate to simple header param 1`] = ` Array [ Object { + "deprecated": false, "description": "a description", "name": "header-name", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -102,6 +109,7 @@ Array [ exports[`params.translator translateToPathParameter should translate 1`] = ` Object { + "deprecated": false, "description": "descr", "name": "name", "required": true, diff --git a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap index 3511832c..c496d16a 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -6,6 +6,10 @@ Object { "in": "body", "name": "param", }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], } `; @@ -16,22 +20,31 @@ Object { "name": "param", "type": "number", }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], } `; exports[`request given single header param should translate to request with header 1`] = ` Object { + "cookie": Array [], "headers": Array [ Object { "in": "header", "name": "param", }, ], + "path": Array [], + "query": Array [], } `; exports[`request given single path param should translate to request with path 1`] = ` Object { + "cookie": Array [], + "headers": Array [], "path": Array [ Object { "in": "path", @@ -39,11 +52,15 @@ Object { "required": true, }, ], + "query": Array [], } `; exports[`request given single query param should translate to request with query 1`] = ` Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], "query": Array [ Object { "in": "query", @@ -55,47 +72,43 @@ Object { exports[`request given two header params should translate 1`] = ` Object { + "cookie": Array [], "headers": Array [ Object { "in": "header", "name": "param", }, - Object { - "in": "header", - "name": "param", - }, ], + "path": Array [], + "query": Array [], } `; exports[`request given two path params should translate 1`] = ` Object { + "cookie": Array [], + "headers": Array [], "path": Array [ Object { "in": "path", "name": "param", "required": true, }, - Object { - "in": "path", - "name": "param", - "required": true, - }, ], + "query": Array [], } `; exports[`request given two query params should translate 1`] = ` Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], "query": Array [ Object { "in": "query", "name": "param", }, - Object { - "in": "query", - "name": "param", - }, ], } `; @@ -107,5 +120,9 @@ Object { "name": "param", "type": "number", }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], } `; diff --git a/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts b/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts deleted file mode 100644 index ceee05df..00000000 --- a/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getExamplesFromSchema } from '../getExamplesFromSchema'; - -describe('getExamplesFromSchema', () => { - it('should ignore invalid data', () => { - // @ts-ignore - expect(getExamplesFromSchema(null)).toEqual({}); - }); - - it('should work with x-examples', () => { - expect( - getExamplesFromSchema({ - 'x-examples': { - 'my-example': {}, - }, - }), - ).toEqual({ - 'my-example': {}, - }); - }); - - it('should work with example', () => { - expect( - getExamplesFromSchema({ - example: 'my-example', - }), - ).toEqual({ - default: 'my-example', - }); - }); -}); diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index a7e70c25..2b8158a8 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -1,15 +1,42 @@ -import { HttpParamStyles } from '@stoplight/types'; -import { FormDataParameter, QueryParameter } from 'swagger-schema-official'; +import { DeepPartial, HttpParamStyles } from '@stoplight/types'; +import { FormDataParameter, QueryParameter, Spec } from 'swagger-schema-official'; +import { createContext } from '../../../context'; import { - translateFromFormDataParameters, - translateToBodyParameter, - translateToHeaderParam, - translateToHeaderParams, - translateToPathParameter, - translateToQueryParameter, + translateFromFormDataParameters as _translateFromFormDataParameters, + translateToBodyParameter as _translateToBodyParameter, + translateToHeaderParam as _translateToHeaderParam, + translateToHeaderParams as _translateToHeaderParams, + translateToPathParameter as _translateToPathParameter, + translateToQueryParameter as _translateToQueryParameter, } from '../params'; +const translateFromFormDataParameters = ( + document: DeepPartial, + ...params: Parameters +) => _translateFromFormDataParameters.call(createContext(document), ...params); + +const translateToBodyParameter = ( + document: DeepPartial, + ...params: Parameters +) => _translateToBodyParameter.call(createContext(document), ...params); + +const translateToHeaderParam = (document: DeepPartial, ...params: Parameters) => + _translateToHeaderParam.call(createContext(document), ...params); + +const translateToHeaderParams = (document: DeepPartial, ...params: Parameters) => + _translateToHeaderParams.call(createContext(document), ...params); + +const translateToPathParameter = ( + document: DeepPartial, + ...params: Parameters +) => _translateToPathParameter.call(createContext(document), ...params); + +const translateToQueryParameter = ( + document: DeepPartial, + ...params: Parameters +) => _translateToQueryParameter.call(createContext(document), ...params); + describe('params.translator', () => { let consumes = ['*']; diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index da7f43c5..286e6788 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -6,6 +6,7 @@ import { QueryParameter, } from 'swagger-schema-official'; +import { createContext } from '../../../context'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -13,13 +14,16 @@ import { translateToPathParameter, translateToQueryParameter, } from '../params'; -import { translateToRequest } from '../request'; +import { translateToRequest as _translateToRequest } from '../request'; jest.mock('../params'); -jest.mock('../../guards'); + +const translateToRequest = (path: Record, parameters: any[]) => { + const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }); + return _translateToRequest.call(ctx, path, { parameters }); +}; describe('request', () => { - const consumes = ['*']; const fakeParameter: FormDataParameter = { name: 'name', type: 'string', @@ -43,45 +47,41 @@ describe('request', () => { jest.resetAllMocks(); }); - it('given empty params collection should return empty object', () => { - expect(translateToRequest({}, [], consumes)).toEqual({}); - }); - it('given single body param should translate to request with body', () => { - expect(translateToRequest({}, [fakeBodyParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeBodyParameter])).toMatchSnapshot(); }); it('given single form param should translate to request with form', () => { - expect(translateToRequest({}, [fakeFormParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeFormParameter])).toMatchSnapshot(); }); it('given single path param should translate to request with path', () => { - expect(translateToRequest({}, [fakePathParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakePathParameter])).toMatchSnapshot(); }); it('given single query param should translate to request with query', () => { - expect(translateToRequest({}, [fakeQueryParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeQueryParameter])).toMatchSnapshot(); }); it('given single header param should translate to request with header', () => { - expect(translateToRequest({}, [fakeHeaderParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeHeaderParameter])).toMatchSnapshot(); }); it('given two query params should translate', () => { - expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter])).toMatchSnapshot(); }); it('given two header params should translate', () => { - expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter])).toMatchSnapshot(); }); it('given two path params should translate', () => { - expect(translateToRequest({}, [fakePathParameter, fakePathParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakePathParameter, fakePathParameter])).toMatchSnapshot(); }); it('should translate mixed request', () => { expect( - translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter], consumes), + translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter]), ).toMatchSnapshot(); }); }); diff --git a/src/oas2/transformers/__tests__/responses.test.ts b/src/oas2/transformers/__tests__/responses.test.ts index 8217087e..11881d59 100644 --- a/src/oas2/transformers/__tests__/responses.test.ts +++ b/src/oas2/transformers/__tests__/responses.test.ts @@ -1,11 +1,15 @@ -import { HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; -import { Schema } from 'swagger-schema-official'; +import { DeepPartial, HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; +import { Operation, Schema, Spec } from 'swagger-schema-official'; +import { createContext } from '../../../context'; import { translateToHeaderParams } from '../params'; -import { translateToResponses } from '../responses'; +import { translateToResponses as _translateToResponses } from '../responses'; jest.mock('../params'); +const translateToResponses = (document: DeepPartial, responses: DeepPartial) => + _translateToResponses.call(createContext(document), { responses }); + describe('responses', () => { const fakeHeaderParams: IHttpHeaderParam[] = [{ name: 'fake-header', style: HttpParamStyles.Simple }]; const produces = ['application/json', 'application/xml']; @@ -16,7 +20,7 @@ describe('responses', () => { it('should translate to multiple responses', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -35,7 +39,6 @@ describe('responses', () => { schema: {}, }, }, - produces, ); expect(responses).toMatchSnapshot(); @@ -44,7 +47,7 @@ describe('responses', () => { it('should translate to response w/o headers', () => { expect( translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -54,7 +57,6 @@ describe('responses', () => { schema: {}, }, }, - produces, ), ).toMatchSnapshot(); }); @@ -62,14 +64,13 @@ describe('responses', () => { it('should translate to response w/o examples', () => { expect( translateToResponses( - {}, + { produces }, { r1: { description: 'd1', schema: {}, }, }, - produces, ), ).toMatchSnapshot(); }); @@ -77,7 +78,7 @@ describe('responses', () => { describe('should keep foreign examples', () => { it('aggregating them to the first example', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -89,7 +90,6 @@ describe('responses', () => { schema: {}, }, }, - produces, ); expect(responses[0].contents).toBeDefined(); @@ -104,7 +104,7 @@ describe('responses', () => { describe('given a response with a schema with an example', () => { it('should translate to response with examples', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -116,7 +116,6 @@ describe('responses', () => { }, }, }, - produces, ); expect(responses[0].contents![0]).toHaveProperty('examples', [{ key: 'default', value: { name: 'value' } }]); }); @@ -125,7 +124,7 @@ describe('responses', () => { describe('given multiple schema example properties', () => { it('should translate all examples', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -142,7 +141,6 @@ describe('responses', () => { } as Schema, }, }, - produces, ); expect(responses[0].contents![0]).toHaveProperty('examples', [ { key: 'application/json', value: { name: 'examples value' } }, @@ -154,7 +152,7 @@ describe('responses', () => { describe('given response with examples in root and schema objects', () => { it('root examples should take precedence over schema examples', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -170,7 +168,6 @@ describe('responses', () => { }, }, }, - produces, ); expect(responses[0].contents![0].examples).toHaveLength(2); expect(responses[0].contents![0].examples).toEqual([ diff --git a/src/oas2/transformers/__tests__/securities.test.ts b/src/oas2/transformers/__tests__/securities.test.ts index 9bd8d382..9c60c069 100644 --- a/src/oas2/transformers/__tests__/securities.test.ts +++ b/src/oas2/transformers/__tests__/securities.test.ts @@ -1,6 +1,11 @@ +import { DeepPartial } from '@stoplight/types'; import { Spec } from 'swagger-schema-official'; -import { translateToSecurities } from '../securities'; +import { createContext } from '../../../context'; +import { translateToSecurities as _translateToSecurities } from '../securities'; + +const translateToSecurities = (document: DeepPartial, ...params: Parameters) => + _translateToSecurities.call(createContext(document), ...params); describe('securities', () => { describe('translateToSecurities', () => { diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 85d52cee..11cd45d6 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,7 +1,14 @@ -import { translateToServers } from '../servers'; +import type { DeepPartial } from '@stoplight/types'; +import type { Spec } from 'swagger-schema-official'; + +import { createContext } from '../../../context'; +import { translateToServers as _translateToServers } from '../servers'; type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; +const translateToServers = (document: DeepPartial, ...params: Parameters) => + _translateToServers.call(createContext(document), ...params); + describe('translateToServers', () => { afterAll(() => { delete (global as GlobalWithLocation).location; diff --git a/src/oas2/transformers/content.ts b/src/oas2/transformers/content.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/oas2/transformers/getExamplesFromSchema.ts b/src/oas2/transformers/getExamplesFromSchema.ts deleted file mode 100644 index b5018f05..00000000 --- a/src/oas2/transformers/getExamplesFromSchema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Dictionary } from '@stoplight/types'; -import { isObject } from 'lodash'; -import { Schema } from 'swagger-schema-official'; - -export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Dictionary { - if (!isObject(data)) return {}; - - return { - ...('x-examples' in data && isObject(data['x-examples']) && { ...data['x-examples'] }), - ...('example' in data && { default: data.example }), - }; -} diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index 7527594f..f1cb5cda 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -1,3 +1,4 @@ +import { isPlainObject } from '@stoplight/json'; import { DeepPartial, HttpParamStyles, @@ -6,26 +7,31 @@ import { IHttpOperationRequestBody, IHttpPathParam, IHttpQueryParam, + Optional, } from '@stoplight/types'; -import { JSONSchema7 } from 'json-schema'; -import { get, map, pick, pickBy, set } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; -import { +import type { JSONSchema7 } from 'json-schema'; +import type { BodyParameter, FormDataParameter, Header, HeaderParameter, PathParameter, QueryParameter, - Spec, } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); +import pick = require('lodash.pick'); +import { isBoolean, isNonNullable, isString } from '../../guards'; +import { Oas2ParamBase } from '../../oas/guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary } from '../../utils'; -import { getExamplesFromSchema } from './getExamplesFromSchema'; +import { ArrayCallbackParameters } from '../../types'; +import { entries } from '../../utils'; +import { getExamplesFromSchema } from '../accessors'; +import { isHeaderParam } from '../guards'; +import { Oas2TranslateFunction } from '../types'; function chooseQueryParameterStyle( - parameter: QueryParameter, + parameter: DeepPartial, ): | HttpParamStyles.PipeDelimited | HttpParamStyles.SpaceDelimited @@ -55,41 +61,40 @@ function chooseQueryParameterStyle( } } -export function translateToHeaderParam(document: DeepPartial, parameter: HeaderParameter): IHttpHeaderParam { - return pickBy({ - ...buildSchemaForParameter(document, parameter), - name: parameter.name, +export const translateToHeaderParam: Oas2TranslateFunction< + [param: DeepPartial & Oas2ParamBase & { in: 'header' }], + IHttpHeaderParam +> = function (parameter) { + return { style: HttpParamStyles.Simple, - required: parameter.required, - }) as unknown as IHttpHeaderParam; -} + name: parameter.name, + ...buildSchemaForParameter.call(this, parameter), + required: !!parameter.required, + }; +}; -export function translateToHeaderParams( - document: DeepPartial, - headers: { [headerName: string]: Header }, -): IHttpHeaderParam[] { - return map(headers, (header, name) => { - const { schema, description } = buildSchemaForParameter(document, Object.assign({ name }, header)); - - const param: IHttpHeaderParam = { - name, - style: HttpParamStyles.Simple, - schema, - description, - }; - - return param; - }); -} +const translateToHeaderParamsFromPair: Oas2TranslateFunction< + ArrayCallbackParameters<[name: string, value: unknown]>, + Optional +> = function ([name, value]) { + if (!isPlainObject(value)) return; + const param = { name, in: 'header', ...value }; + if (!isHeaderParam(param)) return; + return translateToHeaderParam.call(this, param); +}; + +export const translateToHeaderParams: Oas2TranslateFunction<[headers: unknown], IHttpHeaderParam[]> = function ( + headers, +) { + return entries(headers).map(translateToHeaderParamsFromPair, this).filter(isNonNullable); +}; -export function translateToBodyParameter( - document: DeepPartial, - body: BodyParameter, - consumes: string[], -): IHttpOperationRequestBody { - const examples = map( - get(body, 'x-examples') || (body.schema ? getExamplesFromSchema(body.schema) : void 0), - (value, key) => ({ key, value }), +export const translateToBodyParameter: Oas2TranslateFunction< + [body: BodyParameter, consumes: string[]], + IHttpOperationRequestBody +> = function (body, consumes) { + const examples = entries(body['x-examples'] || (body.schema ? getExamplesFromSchema(body.schema) : void 0)).map( + ([key, value]) => ({ key, value }), ); return pickBy({ @@ -98,18 +103,17 @@ export function translateToBodyParameter( contents: consumes.map(mediaType => { return { mediaType, - schema: isDictionary(body.schema) ? translateSchemaObject(document, body.schema) : void 0, + schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, examples, }; }), }); -} +}; -export function translateFromFormDataParameters( - document: DeepPartial, - parameters: FormDataParameter[], - consumes: string[], -): IHttpOperationRequestBody { +export const translateFromFormDataParameters: Oas2TranslateFunction< + [parameters: FormDataParameter[], consumes: string[]], + IHttpOperationRequestBody +> = function (parameters, consumes) { const finalBody: IHttpOperationRequestBody = { contents: consumes.map(mediaType => ({ mediaType, @@ -121,7 +125,7 @@ export function translateFromFormDataParameters( }; return parameters.reduce((body, parameter) => { - const { schema, description } = buildSchemaForParameter(document, parameter); + const { schema, description } = buildSchemaForParameter.call(this, parameter); (body.contents || []).forEach(content => { delete schema.$schema; @@ -129,11 +133,12 @@ export function translateFromFormDataParameters( schema.description = description; } - set(content, `schema.properties.${parameter.name}`, schema); + content.schema ||= {}; + content.schema.properties ||= {}; + content.schema.properties[parameter.name] = schema; if (parameter.required) { - const requiredIndex = get(content, 'schema.required.length', 0); - set(content, `schema.required.${requiredIndex}`, parameter.name); + (content.schema.required ||= []).push(parameter.name); } if (parameter.collectionFormat) { @@ -146,7 +151,7 @@ export function translateFromFormDataParameters( }); return body; }, finalBody); -} +}; function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { switch (parameter.collectionFormat) { @@ -178,29 +183,41 @@ function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { return null; } -export function translateToQueryParameter(document: DeepPartial, query: QueryParameter): IHttpQueryParam { - return pickBy({ - ...buildSchemaForParameter(document, query), - allowEmptyValue: query.allowEmptyValue, - name: query.name, +export const translateToQueryParameter: Oas2TranslateFunction< + [query: DeepPartial & Oas2ParamBase], + IHttpQueryParam +> = function (query) { + return { style: chooseQueryParameterStyle(query), - required: query.required, - }) as unknown as IHttpQueryParam; -} + name: query.name, + required: !!query.required, + ...buildSchemaForParameter.call(this, query), -export function translateToPathParameter(document: DeepPartial, parameter: PathParameter): IHttpPathParam { - return pickBy({ - ...buildSchemaForParameter(document, parameter), - name: parameter.name, + ...pickBy( + { + allowEmptyValue: query.allowEmptyValue, + }, + isBoolean, + ), + }; +}; + +export const translateToPathParameter: Oas2TranslateFunction< + [param: DeepPartial & Oas2ParamBase], + IHttpPathParam +> = function (param) { + return { + name: param.name, style: HttpParamStyles.Simple, - required: parameter.required, - }) as unknown as IHttpPathParam; -} + required: !!param.required, + ...buildSchemaForParameter.call(this, param), + }; +}; -function buildSchemaForParameter( - document: DeepPartial, - param: QueryParameter | PathParameter | HeaderParameter | FormDataParameter | Header, -): { schema: JSONSchema7; description?: string } { +const buildSchemaForParameter: Oas2TranslateFunction< + [param: DeepPartial], + { schema: JSONSchema7; description?: string; deprecated?: boolean } +> = function (param) { const schema = pick( param, 'type', @@ -220,15 +237,21 @@ function buildSchemaForParameter( 'pattern', 'uniqueItems', 'multipleOf', - ); + ) as Record; if ('allowEmptyValue' in param && param.allowEmptyValue === false) { schema.minLength = 1; } return { - schema: translateSchemaObject(document, schema), - description: param.description, - ...('x-deprecated' in param && { deprecated: param['x-deprecated'] }), + schema: translateSchemaObject.call(this, schema), + deprecated: !!param['x-deprecated'], + + ...pickBy( + { + description: param.description, + }, + isString, + ), }; -} +}; diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index e7763ce3..cdf894e3 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,6 +1,13 @@ -import { DeepPartial, IHttpOperationRequest } from '@stoplight/types'; -import { BodyParameter, FormDataParameter, Parameter, Spec } from 'swagger-schema-official'; +import type { IHttpOperationRequest } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import type { BodyParameter, FormDataParameter } from 'swagger-schema-official'; +import { isNonNullable } from '../../guards'; +import { OasVersion } from '../../oas'; +import { getValidOasParameters } from '../../oas/accessors'; +import { getConsumes } from '../accessors'; +import { isHeaderParam, isPathParam, isQueryParam } from '../guards'; +import { Oas2TranslateFunction } from '../types'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -9,38 +16,50 @@ import { translateToQueryParameter, } from './params'; -export function translateToRequest( - document: DeepPartial, - parameters: Parameter[], - consumes: string[], -): IHttpOperationRequest { - const bodyParameters = parameters.filter((p): p is BodyParameter => p.in === 'body'); +export const translateToRequest: Oas2TranslateFunction< + [path: Record, operation: Record], + IHttpOperationRequest +> = function (path, operation) { + const consumes = getConsumes(this.document, operation); + const parameters = getValidOasParameters(this.document, OasVersion.OAS2, operation.parameters, path.parameters); + + const bodyParameter = parameters.find((p): p is BodyParameter => p.in === 'body'); const formDataParameters = parameters.filter((p): p is FormDataParameter => p.in === 'formData'); - const request: IHttpOperationRequest = {}; + const params: Omit, 'body'> = { + headers: [], + query: [], + cookie: [], + path: [], + }; + + let body; // if 'body' and 'form data' defined prefer 'body' - if (!!bodyParameters.length) { + if (!!bodyParameter) { // There can be only one body parameter (taking first one) - request.body = translateToBodyParameter(document, bodyParameters[0], consumes); + body = translateToBodyParameter.call(this, bodyParameter, consumes); } else if (!!formDataParameters.length) { - request.body = translateFromFormDataParameters(document, formDataParameters, consumes); + body = translateFromFormDataParameters.call(this, formDataParameters, consumes); } - return parameters.reduce(createReduceRemainingParameters(document), request); -} - -function createReduceRemainingParameters(document: DeepPartial) { - return function (request: IHttpOperationRequest, parameter: Parameter) { - if (parameter.in === 'query') { - const queryParameter = translateToQueryParameter(document, parameter); - request.query = (request.query || []).concat(queryParameter); - } else if (parameter.in === 'path') { - const pathParameter = translateToPathParameter(document, parameter); - request.path = (request.path || []).concat(pathParameter); - } else if (parameter.in === 'header') { - const headerParameter = translateToHeaderParam(document, parameter); - request.headers = (request.headers || []).concat(headerParameter); + for (const param of parameters) { + if (isQueryParam(param)) { + params.query.push(translateToQueryParameter.call(this, param)); + } else if (isPathParam(param)) { + params.path.push(translateToPathParameter.call(this, param)); + } else if (isHeaderParam(param)) { + params.headers.push(translateToHeaderParam.call(this, param)); } - return request; + } + + return { + ...params, + + ...pickBy( + { + body, + }, + isNonNullable, + ), }; -} +}; diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index 78592e70..34b58bd8 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,34 +1,34 @@ -import type { DeepPartial, Dictionary, IHttpOperationResponse, Optional } from '@stoplight/types'; -import { chain, compact, map, partial } from 'lodash'; -import type { Spec } from 'swagger-schema-official'; +import { isPlainObject } from '@stoplight/json'; +import type { IHttpOperationResponse, Optional } from '@stoplight/types'; +import { DeepPartial } from '@stoplight/types'; +import { Operation } from 'swagger-schema-official'; +import { isNonNullable } from '../../guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import { entries } from '../../utils'; +import { getExamplesFromSchema, getProduces } from '../accessors'; import { isResponseObject } from '../guards'; -import { getExamplesFromSchema } from './getExamplesFromSchema'; +import { Oas2TranslateFunction } from '../types'; import { translateToHeaderParams } from './params'; -function translateToResponse( - document: DeepPartial, - produces: string[], - response: unknown, - statusCode: string, -): Optional { - const resolvedResponse = maybeResolveLocalRef(document, response); +const translateToResponse: Oas2TranslateFunction< + [produces: string[], statusCode: string, response: unknown], + Optional +> = function (produces, statusCode, response) { + const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; - const headers = translateToHeaderParams(document, resolvedResponse.headers || {}); - const objectifiedExamples = chain( + const headers = translateToHeaderParams.call(this, resolvedResponse.headers); + const objectifiedExamples = entries( resolvedResponse.examples || (resolvedResponse.schema ? getExamplesFromSchema(resolvedResponse.schema) : void 0), - ) - .mapValues((value, key) => ({ key, value })) - .values() - .value(); + ).map(([key, value]) => ({ key, value })); const contents = produces .map(produceElement => ({ mediaType: produceElement, - schema: isDictionary(resolvedResponse.schema) ? translateSchemaObject(document, resolvedResponse.schema) : void 0, + schema: isPlainObject(resolvedResponse.schema) + ? translateSchemaObject.call(this, resolvedResponse.schema) + : void 0, examples: objectifiedExamples.filter(example => example.key === produceElement), })) .filter(({ schema, examples }) => !!schema || examples.length > 0); @@ -53,21 +53,14 @@ function translateToResponse( } return translatedResponses; -} +}; -export function translateToResponses( - document: DeepPartial, - responses: unknown, - produces: string[], -): IHttpOperationResponse[] { - if (!isDictionary(responses)) { - return []; - } - - return compact( - map, Optional>( - responses, - partial(translateToResponse, document, produces), - ), - ); -} +export const translateToResponses: Oas2TranslateFunction< + [operation: DeepPartial], + IHttpOperationResponse[] +> = function (operation) { + const produces = getProduces(this.document, operation); + return entries(operation.responses) + .map(([statusCode, response]) => translateToResponse.call(this, produces, statusCode, response)) + .filter(isNonNullable); +}; diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index aaf49c37..c7123286 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -1,141 +1,135 @@ -import { +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial, - Dictionary, - HttpSecurityScheme, IApiKeySecurityScheme, IBasicSecurityScheme, IOauth2SecurityScheme, IOauthFlowObjects, + Optional, } from '@stoplight/types'; -import { isString, pickBy } from 'lodash'; -import { +import pickBy = require('lodash.pickby'); +import type { ApiKeySecurity, + BasicAuthenticationSecurity, OAuth2AccessCodeSecurity, OAuth2ApplicationSecurity, OAuth2ImplicitSecurity, OAuth2PasswordSecurity, - Security, - Spec, } from 'swagger-schema-official'; +import { isNonNullable, isString } from '../../guards'; import { SecurityWithKey } from '../../oas3/accessors'; -import { isDictionary } from '../../utils'; import { getSecurities } from '../accessors'; import { isSecurityScheme } from '../guards'; +import { Oas2TranslateFunction } from '../types'; + +export const translateToFlows: Oas2TranslateFunction<[security: Record], IOauthFlowObjects> = + function (security) { + const flows: IOauthFlowObjects = {}; + + const scopes = isPlainObject(security.scopes) ? pickBy(security.scopes, isString) : {}; + const authorizationUrl = + 'authorizationUrl' in security && typeof security.authorizationUrl === 'string' ? security.authorizationUrl : ''; + const tokenUrl = 'tokenUrl' in security && typeof security.tokenUrl === 'string' ? security.tokenUrl : ''; + + if (security.flow === 'implicit') { + flows.implicit = { + authorizationUrl, + scopes, + }; + } else if (security.flow === 'password') { + flows.password = { + tokenUrl, + scopes, + }; + } else if (security.flow === 'application') { + flows.clientCredentials = { + tokenUrl, + scopes, + }; + } else if (security.flow === 'accessCode') { + flows.authorizationCode = { + authorizationUrl, + tokenUrl, + scopes, + }; + } -/** - * @param security the union with 'any' is purposeful. Passing strict types does not help much here, - * because all these checks happen in runtime. I'm leaving 'Security' only for visibility. - */ -function translateToFlows(security: Dictionary): IOauthFlowObjects { - const flows: IOauthFlowObjects = {}; - - const scopes = isDictionary(security.scopes) ? pickBy(security.scopes, isString) : {}; - const authorizationUrl = - 'authorizationUrl' in security && typeof security.authorizationUrl === 'string' ? security.authorizationUrl : ''; - const tokenUrl = 'tokenUrl' in security && typeof security.tokenUrl === 'string' ? security.tokenUrl : ''; - - if (security.flow === 'implicit') { - flows.implicit = { - authorizationUrl, - scopes, - }; - } else if (security.flow === 'password') { - flows.password = { - tokenUrl, - scopes, - }; - } else if (security.flow === 'application') { - flows.clientCredentials = { - tokenUrl, - scopes, - }; - } else if (security.flow === 'accessCode') { - flows.authorizationCode = { - authorizationUrl, - tokenUrl, - scopes, - }; - } - - return flows; -} + return flows; + }; -function translateToBasicSecurityScheme(security: DeepPartial, key: string): IBasicSecurityScheme { +export const translateToBasicSecurityScheme: Oas2TranslateFunction< + [security: DeepPartial & { key: string }], + IBasicSecurityScheme +> = function (security) { return { type: 'http', scheme: 'basic', description: security.description, - key, + key: security.key, }; -} +}; -function translateToApiKeySecurityScheme( - security: DeepPartial, - key: string, -): IApiKeySecurityScheme | undefined { - const acceptableSecurityOrigins: ApiKeySecurity['in'][] = ['query', 'header']; +const ACCEPTABLE_SECURITY_ORIGINS: ApiKeySecurity['in'][] = ['query', 'header']; - if ('in' in security && security.in && acceptableSecurityOrigins.includes(security.in)) { +export const translateToApiKeySecurityScheme: Oas2TranslateFunction< + [security: DeepPartial & { key: string }], + Optional +> = function (security) { + if ('in' in security && security.in && ACCEPTABLE_SECURITY_ORIGINS.includes(security.in)) { return { type: 'apiKey', - name: security.name || '', in: security.in, + name: String(security.name || ''), description: security.description, - key, + key: security.key, }; } - return undefined; -} + return; +}; const VALID_OAUTH2_FLOWS = ['implicit', 'password', 'application', 'accessCode']; -function translateToOauth2SecurityScheme( - security: DeepPartial< - OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity - >, - key: string, -): IOauth2SecurityScheme | undefined { +export const translateToOauth2SecurityScheme: Oas2TranslateFunction< + [ + security: DeepPartial< + OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity + > & { key: string }, + ], + Optional +> = function (security) { if (!security.flow || !VALID_OAUTH2_FLOWS.includes(security.flow)) return undefined; return { type: 'oauth2', - flows: translateToFlows(security), + flows: translateToFlows.call(this, security), description: security.description, - key, + key: security.key, }; -} +}; -export function translateToSingleSecurity(security: unknown, key: string) { +export const translateToSingleSecurity: Oas2TranslateFunction< + [security: unknown & { key: string }], + Optional +> = function (security) { if (isSecurityScheme(security)) { switch (security.type) { case 'basic': - return translateToBasicSecurityScheme(security, key); + return translateToBasicSecurityScheme.call(this, security); case 'apiKey': - return translateToApiKeySecurityScheme(security, key); + return translateToApiKeySecurityScheme.call(this, security); case 'oauth2': - return translateToOauth2SecurityScheme(security, key); + return translateToOauth2SecurityScheme.call(this, security); } } return; -} - -export function translateToSecurities( - document: DeepPartial, - operationSecurity: Dictionary[] | undefined, -): HttpSecurityScheme[][] { - const securities = getSecurities(document, operationSecurity); - - return securities.map(security => - security.reduce((transformedSecurities, sec) => { - const transformed = translateToSingleSecurity(sec, sec.key); - if (transformed) { - transformedSecurities.push(transformed); - } - - return transformedSecurities; - }, []), - ); -} +}; + +export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = + function (operationSecurities) { + const securities = getSecurities(this.document, operationSecurities); + + return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); + }; diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index 2dee9fd6..7be41c5d 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,32 +1,52 @@ -import { DeepPartial, IServer } from '@stoplight/types'; -import { Operation, Spec } from 'swagger-schema-official'; +import type { IServer, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { isNonNullable, isString } from '../../guards'; import { isValidScheme } from '../guards'; +import type { Oas2TranslateFunction } from '../types'; -export function translateToServers(spec: DeepPartial, operation: DeepPartial): IServer[] { - const { host } = spec; - if (typeof host !== 'string' || host.length === 0) { +export const translateToServers: Oas2TranslateFunction<[operation: Record], IServer[]> = function ( + operation, +) { + let schemes; + if (Array.isArray(operation.schemes)) { + schemes = operation.schemes; + } else if (Array.isArray(this.document.schemes)) { + schemes = this.document.schemes; + } else { return []; } - const schemes = operation.schemes || spec.schemes; - if (!Array.isArray(schemes)) { - return []; + return schemes.map(translateToServer, this).filter(isNonNullable); +}; + +export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optional> = function (scheme) { + const { host } = this.document; + if (typeof host !== 'string' || host.length === 0) { + return; } - const basePath = typeof spec.basePath === 'string' && spec.basePath.length > 0 ? spec.basePath : null; + if (!isString(scheme) || !isValidScheme(scheme)) return; - return schemes.filter(isValidScheme).map(scheme => { - const uri = new URL('https://localhost'); - uri.host = host; - uri.protocol = `${scheme}:`; + const basePath = + typeof this.document.basePath === 'string' && this.document.basePath.length > 0 ? this.document.basePath : null; + + const uri = new URL('https://localhost'); + uri.host = host; + uri.protocol = `${scheme}:`; + + if (basePath !== null) { + uri.pathname = basePath; + } - if (basePath !== null) { - uri.pathname = basePath; - } + return { + url: uri.toString().replace(/\/$/, ''), // Remove trailing slash - return { - url: uri.toString().replace(/\/$/, ''), // Remove trailing slash - }; - }); -} + ...pickBy( + { + name: this.document.info?.title, + }, + isString, + ), + }; +}; diff --git a/src/oas2/types.ts b/src/oas2/types.ts new file mode 100644 index 00000000..dcfe7a17 --- /dev/null +++ b/src/oas2/types.ts @@ -0,0 +1,10 @@ +import type { DeepPartial } from '@stoplight/types'; +import type { Spec } from 'swagger-schema-official'; + +import { TranslateFunction } from '../types'; + +export type Oas2TranslateFunction

= TranslateFunction< + DeepPartial, + P, + R +>; diff --git a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap index b9e22450..92aa3bb3 100644 --- a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap @@ -48,8 +48,6 @@ Object { }, }, ], - "description": undefined, - "required": undefined, }, "cookie": Array [], "headers": Array [], @@ -111,9 +109,8 @@ Object { "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "title", "url": "operation/server", - "variables": undefined, }, ], "summary": "summary", @@ -143,9 +140,8 @@ Object { "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "title", "url": "path/server", - "variables": undefined, }, ], "summary": "summary", @@ -205,9 +201,8 @@ Object { "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "title", "url": "spec/server", - "variables": undefined, }, ], "summary": "summary", diff --git a/src/oas3/__tests__/operation.test.ts b/src/oas3/__tests__/operation.test.ts index 7152e034..fdba8752 100644 --- a/src/oas3/__tests__/operation.test.ts +++ b/src/oas3/__tests__/operation.test.ts @@ -339,7 +339,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - description: void 0, + name: 'title', url: 'operation/server', }, ]); @@ -437,7 +437,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - description: void 0, + name: 'title', url: 'path/server', }, ]); @@ -535,7 +535,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - description: void 0, + name: 'title', url: 'spec/server', }, ]); @@ -642,6 +642,9 @@ describe('transformOas3Operation', () => { }, ], name: 'name', + deprecated: false, + explode: false, + style: 'simple', schema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', @@ -718,6 +721,9 @@ describe('transformOas3Operation', () => { value: 'some example', }, ], + deprecated: false, + explode: false, + style: 'simple', name: 'name', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -787,18 +793,14 @@ describe('transformOas3Operation', () => { variables: { basePath: { default: 'v2', - description: void 0, - enum: void 0, }, port: { default: '8443', - description: void 0, enum: ['8443', '443'], }, username: { default: 'demo', description: 'value is assigned by the service provider', - enum: void 0, }, }, }, @@ -899,9 +901,7 @@ describe('transformOas3Operation', () => { encodings: [], examples: [ { - description: undefined, key: 'pet-shared', - summary: undefined, value: { type: 'object', properties: { @@ -913,12 +913,10 @@ describe('transformOas3Operation', () => { }, }, { - description: undefined, key: 'pet-not-shared', value: { 'not-shared': true, }, - summary: undefined, }, ], mediaType: 'application/json', @@ -929,8 +927,6 @@ describe('transformOas3Operation', () => { }, }, ], - description: undefined, - required: undefined, }, cookie: [], headers: [], @@ -945,9 +941,7 @@ describe('transformOas3Operation', () => { encodings: [], examples: [ { - description: undefined, key: 'pet-shared', - summary: undefined, value: { properties: { id: { @@ -959,9 +953,7 @@ describe('transformOas3Operation', () => { }, }, { - description: undefined, key: 'pet-not-shared', - summary: undefined, value: { 'not-shared': true, }, @@ -975,7 +967,6 @@ describe('transformOas3Operation', () => { }, }, ], - description: undefined, headers: [], }, ], @@ -1251,30 +1242,25 @@ describe('transformOas3Operation', () => { request: { body: { contents: [ - { - encodings: [], - examples: [], - mediaType: 'application/json', + expect.objectContaining({ schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }, + }), ], }, cookie: [], headers: [ - { - examples: [{ key: 'default', value: 'test' }], - name: 'email', + expect.objectContaining({ schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'string', format: 'email', example: 'test', }, - }, + }), ], path: [], query: [], @@ -1283,16 +1269,13 @@ describe('transformOas3Operation', () => { { code: '200', contents: [ - { - encodings: [], - examples: [], - mediaType: 'application/json', + expect.objectContaining({ schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }, + }), ], headers: [], }, diff --git a/src/oas3/__tests__/service.test.ts b/src/oas3/__tests__/service.test.ts index 01ea45b9..523ac541 100644 --- a/src/oas3/__tests__/service.test.ts +++ b/src/oas3/__tests__/service.test.ts @@ -115,12 +115,10 @@ describe('oas3 service', () => { version: '1.0', servers: [ { - description: void 0, name: '', url: 'https://petstore.swagger.io/v2', }, { - description: void 0, name: '', url: 'http://petstore.swagger.io/v2', }, @@ -180,29 +178,23 @@ describe('oas3 service', () => { servers: [ { description: 'Sample Petstore Server Https', - name: '', url: 'https://petstore.swagger.io/v2', variables: { basePath: { default: 'v2', - description: void 0, - enum: void 0, }, port: { default: '8443', - description: void 0, enum: ['8443', '443'], }, username: { default: 'demo', description: 'value is assigned by the service provider', - enum: void 0, }, }, }, { description: 'Sample Petstore Server Http', - name: '', url: 'http://petstore.swagger.io/v2', }, ], diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index c637efff..38f66cf5 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,7 +1,9 @@ +import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, Dictionary, HttpSecurityScheme } from '@stoplight/types'; -import { isObject, mapValues, pickBy } from 'lodash'; -import { OAuthFlowObject, OpenAPIObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import type { OpenAPIObject } from 'openapi3-ts'; +import { entries } from '../utils'; import { isSecurityScheme, isSecuritySchemeWithKey } from './guards'; export type OperationSecurities = Dictionary[] | undefined; @@ -9,14 +11,14 @@ export type SecurityWithKey = HttpSecurityScheme & { key: string }; export function getSecurities( document: DeepPartial, - operationSecurities?: OperationSecurities, + operationSecurities?: unknown, ): SecurityWithKey[][] { const definitions = document.components?.securitySchemes; - if (!isObject(definitions)) return []; + if (!isPlainObject(definitions)) return []; - return (operationSecurities || document.security || []).map(operationSecurity => { - return Object.entries(operationSecurity) + return (Array.isArray(operationSecurities) ? operationSecurities : document.security || []).map(operationSecurity => { + return entries(operationSecurity) .map(([opScheme, scopes]) => { const definition = definitions[opScheme]; @@ -24,10 +26,15 @@ export function getSecurities( // Put back only the flows that are part of the current definition return { ...definition, - flows: mapValues(definition.flows, (flow: OAuthFlowObject) => ({ - ...flow, - scopes: pickBy(flow.scopes, (_val: string, key: string) => scopes?.includes(key)), - })), + flows: Object.fromEntries( + entries(definition.flows).map(([name, flow]) => [ + name, + { + ...flow, + scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), + }, + ]), + ), key: opScheme, }; } diff --git a/src/oas3/guards.ts b/src/oas3/guards.ts index c2cdf920..3bac2280 100644 --- a/src/oas3/guards.ts +++ b/src/oas3/guards.ts @@ -1,6 +1,5 @@ -import { Dictionary } from '@stoplight/types'; -import { isObject } from 'lodash'; -import { +import { isPlainObject } from '@stoplight/json'; +import type { BaseParameterObject, HeaderObject, OAuthFlowObject, @@ -12,19 +11,18 @@ import { TagObject, } from 'openapi3-ts'; -import { isDictionary } from '../utils'; -import { SecurityWithKey } from './accessors'; +import type { SecurityWithKey } from './accessors'; export const isSecurityScheme = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecuritySchemeObject => - isObject(maybeSecurityScheme) && typeof (maybeSecurityScheme as Dictionary).type === 'string'; + isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; export const isSecuritySchemeWithKey = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecurityWithKey => - isSecurityScheme(maybeSecurityScheme) && typeof (maybeSecurityScheme as Dictionary).key === 'string'; + isSecurityScheme(maybeSecurityScheme) && typeof maybeSecurityScheme.key === 'string'; export const isBaseParameterObject = ( maybeBaseParameterObject: unknown, ): maybeBaseParameterObject is BaseParameterObject => - isObject(maybeBaseParameterObject) && + isPlainObject(maybeBaseParameterObject) && ('description' in maybeBaseParameterObject || 'required' in maybeBaseParameterObject || 'content' in maybeBaseParameterObject || @@ -38,33 +36,33 @@ export const isHeaderObject = (maybeHeaderObject: unknown): maybeHeaderObject is isBaseParameterObject(maybeHeaderObject); export const isServerObject = (maybeServerObject: unknown): maybeServerObject is ServerObject => - isObject(maybeServerObject) && typeof (maybeServerObject as Dictionary).url === 'string'; + isPlainObject(maybeServerObject) && typeof maybeServerObject.url === 'string'; export const isServerVariableObject = ( maybeServerVariableObject: unknown, ): maybeServerVariableObject is ServerVariableObject => { - if (!isObject(maybeServerVariableObject)) return false; - const typeofDefault = typeof (maybeServerVariableObject as Dictionary).default; + if (!isPlainObject(maybeServerVariableObject)) return false; + const typeofDefault = typeof maybeServerVariableObject.default; return typeofDefault === 'string' || typeofDefault === 'boolean' || typeofDefault === 'number'; }; export const isTagObject = (maybeTagObject: unknown): maybeTagObject is TagObject => { - if (isObject(maybeTagObject) && 'name' in maybeTagObject) { - return typeof (maybeTagObject as TagObject).name === 'string'; + if (isPlainObject(maybeTagObject) && 'name' in maybeTagObject) { + return typeof maybeTagObject.name === 'string'; } return false; }; export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObject is ResponseObject => - isObject(maybeResponseObject) && + isPlainObject(maybeResponseObject) && ('description' in maybeResponseObject || 'headers' in maybeResponseObject || 'content' in maybeResponseObject || 'links' in maybeResponseObject); export const isOAuthFlowObject = (maybeOAuthFlowObject: unknown): maybeOAuthFlowObject is OAuthFlowObject => - isDictionary(maybeOAuthFlowObject) && isDictionary(maybeOAuthFlowObject.scopes); + isPlainObject(maybeOAuthFlowObject) && isPlainObject(maybeOAuthFlowObject.scopes); export const isRequestBodyObject = (maybeRequestBodyObject: unknown): maybeRequestBodyObject is RequestBodyObject => - isDictionary(maybeRequestBodyObject) && isDictionary(maybeRequestBodyObject.content); + isPlainObject(maybeRequestBodyObject) && isPlainObject(maybeRequestBodyObject.content); diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index 28617ada..f423a26c 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,13 +1,14 @@ import { IHttpOperation } from '@stoplight/types'; -import { get, isNil, omitBy } from 'lodash'; -import type { OpenAPIObject, OperationObject, ParameterObject, PathsObject, RequestBodyObject } from 'openapi3-ts'; +import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import { createContext } from '../context'; +import { isNonNullable } from '../guards'; import { transformOasOperations } from '../oas'; -import { getExtensions, getOasTags, getValidOasParameters } from '../oas/accessors'; -import { translateToTags } from '../oas/tag'; -import { Oas3HttpOperationTransformer } from '../oas/types'; +import { getExtensions } from '../oas/accessors'; +import { translateToTags } from '../oas/tags'; +import type { Oas3HttpOperationTransformer } from '../oas/types'; import { maybeResolveLocalRef } from '../utils'; -import { isServerObject } from './guards'; import { translateToCallbacks } from './transformers/callbacks'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; @@ -19,7 +20,7 @@ export function transformOas3Operations(document: OpenAPIObject): IHttpOperation } export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, get(document, ['paths', path])) as PathsObject; + const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]) as PathsObject; if (typeof pathObj !== 'object' || pathObj === null) { throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); } @@ -29,7 +30,7 @@ export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); } - const servers = operation.servers || pathObj.servers || document.servers; + const ctx = createContext(document); const httpOperation: IHttpOperation = { id: '?http-operation-id?', @@ -40,18 +41,14 @@ export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, method, path, summary: operation.summary, - responses: translateToResponses(document, operation.responses), - servers: Array.isArray(servers) ? translateToServers(servers.filter(isServerObject)) : [], - request: translateToRequest( - document, - getValidOasParameters(document, operation.parameters as ParameterObject[], pathObj.parameters), - operation.requestBody as RequestBodyObject, - ), - callbacks: operation.callbacks && translateToCallbacks(operation.callbacks), - tags: translateToTags(getOasTags(operation.tags)), - security: translateToSecurities(document, operation.security), + responses: translateToResponses.call(ctx, operation.responses), + request: translateToRequest.call(ctx, pathObj, operation), + callbacks: translateToCallbacks.call(ctx, operation.callbacks), + tags: translateToTags.call(ctx, operation.tags), + security: translateToSecurities.call(ctx, operation.security), extensions: getExtensions(operation), + servers: translateToServers.call(ctx, pathObj, operation), }; - return omitBy(httpOperation, isNil) as IHttpOperation; + return pickBy(httpOperation, isNonNullable) as IHttpOperation; }; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index 9626116e..acce098f 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,34 +1,27 @@ -import { HttpSecurityScheme, IHttpService, IServer } from '@stoplight/types'; -import { compact, filter, flatMap, keys, map, pickBy } from 'lodash'; - -import { hasXLogo } from '../oas/guards'; -import { translateLogo } from '../oas/transformers/translateLogo'; -import { Oas3HttpServiceTransformer } from '../oas/types'; -import { isDictionary } from '../utils'; -import { isSecurityScheme, isTagObject } from './guards'; -import { transformToSingleSecurity } from './transformers/securities'; -import { translateServerVariables } from './transformers/servers'; +import { isPlainObject } from '@stoplight/json'; +import type { Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { createContext } from '../context'; +import { isNonNullable } from '../guards'; +import { transformOasService } from '../oas/service'; +import type { Oas3HttpServiceTransformer } from '../oas/types'; +import { ArrayCallbackParameters } from '../types'; +import { entries } from '../utils'; +import { SecurityWithKey } from './accessors'; +import { isSecurityScheme } from './guards'; +import { translateToSingleSecurity } from './transformers/securities'; +import { translateToServer } from './transformers/servers'; +import { Oas3TranslateFunction } from './types'; export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) => { - const httpService: IHttpService = { - id: '?http-service-id?', - version: document.info?.version ?? '', - name: document.info?.title ?? 'no-title', - }; - - if (typeof document.info?.description === 'string') { - httpService.description = document.info.description; - } + const ctx = createContext(document); + const httpService = transformOasService.call(ctx); if (typeof document.info?.summary === 'string') { httpService.summary = document.info.summary; } - if (document.info?.contact) { - httpService.contact = document.info.contact; - } - - if (typeof document.info?.license === 'object' && document.info.license !== null) { + if (document.info?.license) { const { name, identifier, ...license } = document.info.license; httpService.license = { ...license, @@ -37,50 +30,27 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = }; } - if (document.info?.termsOfService) { - httpService.termsOfService = document.info.termsOfService; - } - - if (isDictionary(document.info) && hasXLogo(document.info)) { - httpService.logo = translateLogo(document.info); - } - - const servers = compact( - map(document.servers, server => { - if (!server) return null; + const servers = Array.isArray(document.servers) + ? document.servers.map(translateToServer, ctx).filter(isNonNullable) + : []; - const serv: IServer = { - name: document.info?.title ?? '', - description: server.description, - url: server.url ?? '', - }; - - const variables = server.variables && translateServerVariables(server.variables); - if (variables && Object.keys(variables).length) serv.variables = variables; - - return serv; - }), - ); if (servers.length) { httpService.servers = servers; } - const securitySchemes = compact( - keys(document.components?.securitySchemes).map(key => { - const definition = document?.components?.securitySchemes?.[key]; - return isSecurityScheme(definition) && transformToSingleSecurity(definition, key); - }), - ); + const securitySchemes = entries(document.components?.securitySchemes) + .map(translateSecurityScheme, ctx) + .filter(isNonNullable); if (securitySchemes.length) { httpService.securitySchemes = securitySchemes; } - const security = compact( - flatMap(document.security, sec => { - if (!sec) return null; + const security = (Array.isArray(document.security) ? document.security : []) + .flatMap(sec => { + if (!isPlainObject(sec)) return null; - return keys(sec).map(key => { + return Object.keys(sec).map(key => { const ss = securitySchemes.find(securityScheme => securityScheme.key === key); if (ss && ss.type === 'oauth2') { const flows = {}; @@ -104,18 +74,26 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = return ss; }); - }), - ); + }) + .filter(isNonNullable); if (security.length) { - //@ts-ignore I hate doing this, but unfortunately Lodash types are (rightfully) very loose and put undefined when it can't happen httpService.security = security; } - const tags = filter(document.tags, isTagObject); - if (tags.length) { - httpService.tags = tags; + return httpService; +}; + +const translateSecurityScheme: Oas3TranslateFunction< + ArrayCallbackParameters<[name: string, scheme: unknown]>, + Optional +> = function ([key, definition]) { + if (!isSecurityScheme(definition)) return; + + const transformed = translateToSingleSecurity.call(this, definition); + if (transformed && 'key' in transformed) { + transformed.key = key; } - return httpService; + return transformed; }; diff --git a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap index 22c29f5d..04e8e103 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap @@ -4,12 +4,12 @@ exports[`translateHeaderObject should translate to IHttpHeaderParam 1`] = ` Object { "allowEmptyValue": true, "allowReserved": true, + "content": Object {}, "deprecated": true, "description": "descr", "encodings": Array [], "examples": Array [ Object { - "description": undefined, "key": "a", "summary": "example summary", "value": "hey", @@ -22,8 +22,10 @@ Object { "explode": true, "name": "header-name", "required": true, - "schema": Object {}, - "style": "matrix", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, + "style": "simple", } `; @@ -31,21 +33,24 @@ exports[`translateMediaTypeObject given complex nested media type object should Object { "encodings": Array [ Object { - "contentType": "text/plain", + "explode": true, "headers": Array [ Object { + "content": Object { + "nested/media": Object {}, + }, "description": "descr", "encodings": Array [], "examples": Array [ Object { - "description": undefined, "key": "a", "summary": "example summary", "value": "hey", }, ], + "explode": false, "name": "h1", - "style": "matrix", + "style": "simple", }, ], "mediaType": "text/plain", @@ -55,7 +60,6 @@ Object { ], "examples": Array [ Object { - "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -68,40 +72,11 @@ Object { } `; -exports[`translateMediaTypeObject given complex nested media type object with nullish headers should translate correctly 1`] = ` -Object { - "encodings": Array [ - Object { - "contentType": "text/plain", - "headers": Array [], - "mediaType": "text/plain", - "property": "enc1", - "style": "form", - }, - ], - "examples": Array [ - Object { - "description": undefined, - "key": "example", - "summary": "multi example", - "value": "hey", - }, - ], - "mediaType": "mediaType", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, -} -`; - -exports[`translateMediaTypeObject given encoding with incorrect style should throw an error 1`] = `"Encoding property style: 'xyz' is incorrect, must be one of: form,spaceDelimited,pipeDelimited,deepObject"`; - exports[`translateMediaTypeObject given encodings should translate each encoding to array item 1`] = ` Object { "encodings": Array [ Object { "allowReserved": true, - "contentType": "text/plain", "explode": true, "headers": Array [ Object { @@ -117,7 +92,6 @@ Object { }, Object { "allowReserved": true, - "contentType": "text/plain", "explode": true, "headers": Array [ Object { @@ -134,7 +108,6 @@ Object { ], "examples": Array [ Object { - "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -152,7 +125,6 @@ Object { "encodings": Array [], "examples": Array [ Object { - "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -180,3 +152,28 @@ Object { }, } `; + +exports[`translateMediaTypeObject should skip nullish headers 1`] = ` +Object { + "encodings": Array [ + Object { + "explode": true, + "headers": Array [], + "mediaType": "text/plain", + "property": "enc1", + "style": "form", + }, + ], + "examples": Array [ + Object { + "key": "example", + "summary": "multi example", + "value": "hey", + }, + ], + "mediaType": "mediaType", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, +} +`; diff --git a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap index 164c80f4..4afeb24b 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -4,7 +4,7 @@ exports[`translateOas3ToRequest give a request body should translate it 1`] = ` Object { "body": Object { "contents": Array [ - "translateMediaTypeObject({}, {\\"schema\\":{\\"deprecated\\":true}}, content-a, {\\"content-a\\":{\\"schema\\":{\\"deprecated\\":true}}})", + "translateMediaTypeObject([\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}], 0, [[\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}]])", ], "description": "descr", "required": true, @@ -29,33 +29,41 @@ Object { "schema": Object {}, }, }, + "deprecated": false, "description": "descr", "examples": Array [], + "explode": false, "name": "param-name-3", + "style": "simple", }, ], "path": Array [], "query": Array [ Object { "content": Object { - "content-a": Object { + "content-b": Object { "schema": Object {}, }, }, - "deprecated": true, + "deprecated": false, "description": "descr", "examples": Array [], - "name": "param-name-1", + "explode": false, + "name": "param-name-2", + "style": "simple", }, Object { "content": Object { - "content-b": Object { + "content-a": Object { "schema": Object {}, }, }, + "deprecated": true, "description": "descr", "examples": Array [], - "name": "param-name-2", + "explode": false, + "name": "param-name-1", + "style": "simple", }, ], } diff --git a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap index 90fa85a6..54946b4f 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -14,7 +14,6 @@ Array [ }, ], "mediaType": "fake-content-type-200", - "schema": undefined, }, ], "description": "descr 200", @@ -34,7 +33,6 @@ Array [ "encodings": Array [], "examples": Array [], "mediaType": "fake-content-type", - "schema": undefined, }, ], "description": "descr", @@ -48,9 +46,13 @@ Array [ "value": 1000, }, ], + "explode": false, "name": "fake-header-name-1", "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, "type": "integer", }, "style": "simple", @@ -64,10 +66,14 @@ Array [ "value": 1000, }, ], + "explode": false, "name": "fake-header-name-2", "required": true, "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, "type": "integer", }, "style": "simple", diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index 05fef5b7..d22bf44a 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -1,6 +1,16 @@ -import { SchemaObject } from 'openapi3-ts'; +import type { SchemaObject } from 'openapi3-ts'; -import { translateHeaderObject, translateMediaTypeObject } from '../content'; +import { createContext } from '../../../context'; +import { + translateHeaderObject as _translateHeaderObject, + translateMediaTypeObject as _translateMediaTypeObject, +} from '../content'; + +const translateMediaTypeObject = (document: any, object: unknown, key: string) => + _translateMediaTypeObject.call(createContext(document), [key, object], 0, []); + +const translateHeaderObject = (object: unknown, key: string) => + _translateHeaderObject.call(createContext({}), [key, object], 0, []); describe('translateMediaTypeObject', () => { afterEach(() => { @@ -16,28 +26,6 @@ describe('translateMediaTypeObject', () => { encodings: [], examples: [], mediaType: 'mediaType', - schema: void 0, - }); - }); - - it('given invalid schema, should return nothing', () => { - expect( - translateMediaTypeObject( - {}, - { - schema: { - get properties() { - throw new Error('I am invalid'); - }, - }, - }, - 'mediaType', - ), - ).toStrictEqual({ - encodings: [], - examples: [], - mediaType: 'mediaType', - schema: void 0, }); }); @@ -54,7 +42,6 @@ describe('translateMediaTypeObject', () => { encodings: [], examples: [], mediaType: 'mediaType', - schema: void 0, }); }); @@ -159,7 +146,7 @@ describe('translateMediaTypeObject', () => { ).toMatchSnapshot(); }); - it('given complex nested media type object with nullish headers should translate correctly', () => { + it('should skip nullish headers', () => { expect( translateMediaTypeObject( {}, @@ -181,26 +168,6 @@ describe('translateMediaTypeObject', () => { ).toMatchSnapshot(); }); - it('given encoding with incorrect style should throw an error', () => { - const testedFunction = () => { - translateMediaTypeObject( - {}, - { - schema: {}, - examples: { example: { summary: 'multi example' } }, - encoding: { - enc1: { - contentType: 'text/plain', - style: 'xyz', - }, - }, - }, - 'mediaType', - ); - }; - expect(testedFunction).toThrowErrorMatchingSnapshot(); - }); - it('given encoding with no style it should not throw an error', () => { const testedFunction = () => { translateMediaTypeObject( diff --git a/src/oas3/transformers/__tests__/request.test.ts b/src/oas3/transformers/__tests__/request.test.ts index bc7bc201..0be85e70 100644 --- a/src/oas3/transformers/__tests__/request.test.ts +++ b/src/oas3/transformers/__tests__/request.test.ts @@ -1,56 +1,70 @@ import { mockPassthroughImplementation } from '@stoplight/test-utils'; +import { createContext } from '../../../context'; import { translateMediaTypeObject } from '../content'; -import { translateToRequest } from '../request'; +import { translateToRequest as _translateToRequest } from '../request'; jest.mock('../content'); +const translateToRequest = (path: Record, operation: Record) => { + const ctx = createContext({ paths: { '/api': path } }); + return _translateToRequest.call(ctx, path, operation); +}; + describe('translateOas3ToRequest', () => { beforeEach(() => { mockPassthroughImplementation(translateMediaTypeObject); }); it('given no request body should translate parameters', () => { - expect( - translateToRequest({}, [ + const operation = { + parameters: [ { - name: 'param-name-1', + name: 'param-name-2', in: 'query', description: 'descr', - deprecated: true, content: { - 'content-a': { + 'content-b': { schema: {}, }, }, }, { - name: 'param-name-2', - in: 'query', + name: 'param-name-3', + in: 'header', description: 'descr', content: { - 'content-b': { + 'content-c': { schema: {}, }, }, }, + ], + }; + + const path = { + parameters: [ { - name: 'param-name-3', - in: 'header', + name: 'param-name-1', + in: 'query', description: 'descr', + deprecated: true, content: { - 'content-c': { + 'content-a': { schema: {}, }, }, }, - ]), - ).toMatchSnapshot(); + ], + get: operation, + }; + + expect(translateToRequest(path, operation)).toMatchSnapshot(); }); it('give a request body should translate it', () => { - expect( - translateToRequest({}, [], { + const operation = { + requestBody: { description: 'descr', required: true, content: { @@ -60,7 +74,13 @@ describe('translateOas3ToRequest', () => { }, }, }, - }), - ).toMatchSnapshot(); + }, + }; + + const path = { + post: operation, + }; + + expect(translateToRequest(path, operation)).toMatchSnapshot(); }); }); diff --git a/src/oas3/transformers/__tests__/responses.test.ts b/src/oas3/transformers/__tests__/responses.test.ts index 0d8154fd..9f5a929d 100644 --- a/src/oas3/transformers/__tests__/responses.test.ts +++ b/src/oas3/transformers/__tests__/responses.test.ts @@ -1,7 +1,11 @@ import type { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { translateToResponses } from '../responses'; +import { createContext } from '../../../context'; +import { translateToResponses as _translateToResponses } from '../responses'; + +const translateToResponses = (document: DeepPartial, responses: unknown) => + _translateToResponses.call(createContext(document), responses); describe('translateToOas3Responses', () => { it('given empty dictionary should return empty array', () => { @@ -70,7 +74,6 @@ describe('translateToOas3Responses', () => { { code: '200', contents: [], - description: void 0, headers: [], }, ]); @@ -212,10 +215,12 @@ describe('translateToOas3Responses', () => { { name: 'X-Page', schema: { + $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', }, style: 'simple', description: 'Current page (if pagination parameters were provided in the request)', + explode: false, encodings: [], examples: [ { diff --git a/src/oas3/transformers/__tests__/securities.test.ts b/src/oas3/transformers/__tests__/securities.test.ts index 0bf47a08..b682c291 100644 --- a/src/oas3/transformers/__tests__/securities.test.ts +++ b/src/oas3/transformers/__tests__/securities.test.ts @@ -1,6 +1,12 @@ +import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { translateToSecurities } from '../securities'; +import { createContext } from '../../../context'; +import { OperationSecurities } from '../../accessors'; +import { translateToSecurities as _translateToSecurities } from '../securities'; + +const translateToSecurities = (document: DeepPartial, operationSecurities: OperationSecurities) => + _translateToSecurities.call(createContext(document), operationSecurities); describe('securities', () => { describe('translateToSecurities', () => { diff --git a/src/oas3/transformers/__tests__/servers.test.ts b/src/oas3/transformers/__tests__/servers.test.ts index 3e396d77..d6573d06 100644 --- a/src/oas3/transformers/__tests__/servers.test.ts +++ b/src/oas3/transformers/__tests__/servers.test.ts @@ -1,9 +1,17 @@ -import { translateToServers } from '../servers'; +import { DeepPartial } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; + +import { createContext } from '../../../context'; +import { translateToServers as _translateToServers } from '../servers'; + +const translateToServers = ( + document: DeepPartial & { paths: { '/pet': { get: Record } } }, +) => _translateToServers.call(createContext(document), document.paths['/pet'], document.paths['/pet'].get); describe('translateToServers', () => { it('translate single ServerObject to IServer', () => { - expect( - translateToServers([ + const document = { + servers: [ { description: 'description', url: 'http://stoplight.io/path', @@ -20,13 +28,20 @@ describe('translateToServers', () => { }, }, }, - ]), - ).toMatchSnapshot(); + ], + paths: { + '/pet': { + get: {}, + }, + }, + }; + + expect(translateToServers(document)).toMatchSnapshot(); }); it('filters out invalid variables', () => { - expect( - translateToServers([ + const document = { + servers: [ { description: 'description', url: 'http://stoplight.io/path', @@ -43,8 +58,15 @@ describe('translateToServers', () => { }, } as any, }, - ]), - ).toStrictEqual([ + ], + paths: { + '/pet': { + get: {}, + }, + }, + }; + + expect(translateToServers(document)).toStrictEqual([ { description: 'description', url: 'http://stoplight.io/path', @@ -58,4 +80,71 @@ describe('translateToServers', () => { }, ]); }); + + it('prefers operation servers over any other servers', () => { + const document = { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/path', + }, + ], + paths: { + '/pet': { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/pet', + }, + ], + + get: { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/pet.get', + }, + ], + }, + }, + }, + }; + + expect(translateToServers(document)).toStrictEqual([ + { + description: 'description', + url: 'http://stoplight.io/pet.get', + }, + ]); + }); + + it('given missing operation servers, prefers path servers over any document servers', () => { + const document = { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/path', + }, + ], + paths: { + '/pet': { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/pet', + }, + ], + + get: {}, + }, + }, + }; + + expect(translateToServers(document)).toStrictEqual([ + { + description: 'description', + url: 'http://stoplight.io/pet', + }, + ]); + }); }); diff --git a/src/oas3/transformers/callbacks.ts b/src/oas3/transformers/callbacks.ts index c97e4bf5..422cdbd8 100644 --- a/src/oas3/transformers/callbacks.ts +++ b/src/oas3/transformers/callbacks.ts @@ -1,33 +1,35 @@ -import { IHttpCallbackOperation } from '@stoplight/types'; -import { entries } from 'lodash'; -import { CallbacksObject, OpenAPIObject } from 'openapi3-ts'; +import type { IHttpCallbackOperation } from '@stoplight/types'; +import type { OpenAPIObject } from 'openapi3-ts'; +import { entries } from '../../utils'; import { transformOas3Operation } from '../operation'; +import type { Oas3TranslateFunction } from '../types'; -export function translateToCallbacks(callbacks: CallbacksObject): IHttpCallbackOperation[] | undefined { - const callbackEntries = entries(callbacks); - if (!callbackEntries.length) return; +export const translateToCallbacks: Oas3TranslateFunction<[callbacks: unknown], IHttpCallbackOperation[] | undefined> = + function (callbacks) { + const callbackEntries = entries(callbacks); + if (!callbackEntries.length) return; - return callbackEntries.reduce((results: IHttpCallbackOperation[], [callbackName, path2Methods]) => { - for (const [path, method2Op] of entries(path2Methods)) { - for (const [method, op] of entries(method2Op as { [key: string]: {} })) { - const document: Partial = { - openapi: '3', - info: { title: '', version: '1' }, - paths: { [path]: { [method]: op } }, - }; + return callbackEntries.reduce((results: IHttpCallbackOperation[], [callbackName, path2Methods]) => { + for (const [path, method2Op] of entries(path2Methods)) { + for (const [method, op] of entries(method2Op as { [key: string]: {} })) { + const document: Partial = { + openapi: '3', + info: { title: '', version: '1' }, + paths: { [path]: { [method]: op } }, + }; - results.push({ - ...transformOas3Operation({ - document, - method, - path, - }), - callbackName, - }); + results.push({ + ...transformOas3Operation({ + document, + method, + path, + }), + callbackName, + }); + } } - } - return results; - }, []); -} + return results; + }, []); + }; diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index 696e31a1..7b33152f 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -1,5 +1,5 @@ +import { isPlainObject } from '@stoplight/json'; import { - DeepPartial, Dictionary, HttpParamStyles, IHttpEncoding, @@ -8,48 +8,69 @@ import { INodeExample, Optional, } from '@stoplight/types'; -import { JSONSchema7 } from 'json-schema'; -import { compact, each, get, isObject, keys, map, omit, pickBy, union, values } from 'lodash'; -import { EncodingPropertyObject, HeaderObject, MediaTypeObject, OpenAPIObject } from 'openapi3-ts'; +import type { JSONSchema7 } from 'json-schema'; +import pickBy = require('lodash.pickby'); +import { isBoolean, isNonNullable, isString } from '../../guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import { ArrayCallbackParameters, Fragment } from '../../types'; +import { entries, maybeResolveLocalRef } from '../../utils'; import { isHeaderObject } from '../guards'; +import { Oas3TranslateFunction } from '../types'; + +const ACCEPTABLE_STYLES: (string | undefined)[] = [ + HttpParamStyles.Form, + HttpParamStyles.SpaceDelimited, + HttpParamStyles.PipeDelimited, + HttpParamStyles.DeepObject, +]; + +function isAcceptableStyle( + encodingPropertyObject: T, +): encodingPropertyObject is T & { + style: + | HttpParamStyles.Form + | HttpParamStyles.SpaceDelimited + | HttpParamStyles.PipeDelimited + | HttpParamStyles.DeepObject; +} { + return typeof encodingPropertyObject.style === 'string' && ACCEPTABLE_STYLES.includes(encodingPropertyObject.style); +} -function translateEncodingPropertyObject( - encodingPropertyObject: EncodingPropertyObject, - property: string, -): IHttpEncoding { - const acceptableStyles: (string | undefined)[] = [ - HttpParamStyles.Form, - HttpParamStyles.SpaceDelimited, - HttpParamStyles.PipeDelimited, - HttpParamStyles.DeepObject, - ]; - - if (encodingPropertyObject.style && !acceptableStyles.includes(encodingPropertyObject.style)) { - throw new Error( - `Encoding property style: '${encodingPropertyObject.style}' is incorrect, must be one of: ${acceptableStyles}`, - ); - } +const translateEncodingPropertyObject: Oas3TranslateFunction< + ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, + Optional +> = function ([property, encodingPropertyObject]) { + if (!isPlainObject(encodingPropertyObject)) return; + if (!isAcceptableStyle(encodingPropertyObject)) return; return { property, - ...encodingPropertyObject, - // workaround for 'style' being one of the accepted HttpParamStyles - style: encodingPropertyObject.style as any, - mediaType: encodingPropertyObject.contentType, - headers: compact( - map & unknown, Optional>( - encodingPropertyObject.headers, - translateHeaderObject, - ), + explode: !!(encodingPropertyObject.explode ?? encodingPropertyObject.style === HttpParamStyles.Form), + style: encodingPropertyObject.style, + headers: entries(encodingPropertyObject.headers).map(translateHeaderObject, this).filter(isNonNullable), + + ...pickBy( + { + allowReserved: encodingPropertyObject.allowReserved, + }, + isBoolean, + ), + + ...pickBy( + { + mediaType: encodingPropertyObject.contentType, + }, + isString, ), }; -} +}; -export function translateHeaderObject(headerObject: unknown, name: string): Optional { - if (!isObject(headerObject)) return; +export const translateHeaderObject = < + Oas3TranslateFunction, Optional> +>function ([name, unresolvedHeaderObject]) { + const headerObject = this.maybeResolveLocalRef(unresolvedHeaderObject); + if (!isPlainObject(headerObject)) return; if (!isHeaderObject(headerObject)) { return { @@ -62,116 +83,150 @@ export function translateHeaderObject(headerObject: unknown, name: string): Opti const { content: contentObject } = headerObject; - const contentValue = values(contentObject)[0]; + const contentValue = isPlainObject(contentObject) ? Object.values(contentObject)[0] : null; - const baseContent = { - // TODO(SL-249): we are missing examples in our types, on purpose? - // examples: parameterObject.examples, - ...omit(headerObject, 'content', 'style', 'examples', 'example', 'schema'), + const baseContent: IHttpHeaderParam = { name, - style: headerObject?.style ?? HttpParamStyles.Simple, + style: HttpParamStyles.Simple, + explode: !!headerObject.explode, + + ...pickBy( + { + schema: isPlainObject(headerObject.schema) ? translateSchemaObject.call(this, headerObject.schema) : null, + content: headerObject.content, + }, + isNonNullable, + ), + + ...pickBy( + { + description: headerObject.description, + }, + isString, + ), + + ...pickBy( + { + allowEmptyValue: headerObject.allowEmptyValue, + allowReserved: headerObject.allowReserved, + + required: headerObject.required, + deprecated: headerObject.deprecated, + }, + isBoolean, + ), }; const examples: INodeExample[] = []; const encodings: IHttpEncoding[] = []; - if (contentValue) { - examples.push(...keys(contentValue.examples).map(transformExamples(contentValue))); + if (isPlainObject(contentValue)) { + examples.push(...entries(contentValue.examples).map(translateToExample, this).filter(isNonNullable)); - encodings.push(...values(contentValue.encoding)); + if (isPlainObject(contentValue.encoding)) { + encodings.push(...(Object.values(contentValue.encoding) as IHttpEncoding[])); + } - if (contentValue.example) { - examples.push({ - key: '__default_content', - value: contentValue.example, - }); + if ('example' in contentValue) { + examples.push(transformDefaultExample.call(this, '__default_content', contentValue.example)); } } - examples.push(...keys(headerObject.examples).map(transformExamples(headerObject))); + examples.push(...entries(headerObject.examples).map(translateToExample, this).filter(isNonNullable)); - if (headerObject.example) { - examples.push({ - key: '__default', - value: headerObject.example, - }); + if ('example' in headerObject) { + examples.push(transformDefaultExample.call(this, '__default', headerObject.example)); } - return pickBy({ + return { ...baseContent, - schema: get(headerObject, 'schema') as any, encodings, examples, - }) as unknown as IHttpHeaderParam; -} + }; +}; -export function translateMediaTypeObject( - document: DeepPartial, - mediaObject: unknown, - mediaType: string, -): Optional { - if (!isDictionary(mediaObject)) return; +const translateSchemaMediaTypeObject: Oas3TranslateFunction<[schema: unknown], Optional> = function ( + schema, +) { + if (!isPlainObject(schema)) return; - const resolvedMediaObject = resolveMediaObject(document, mediaObject); - const { schema, encoding, examples } = resolvedMediaObject; + return translateSchemaObject.call(this, schema); +}; - let jsonSchema: Optional; +export const translateMediaTypeObject: Oas3TranslateFunction< + ArrayCallbackParameters<[mediaType: string, mediaObject: unknown]>, + Optional +> = function ([mediaType, mediaObject]) { + if (!isPlainObject(mediaObject)) return; - if (isObject(schema)) { - try { - jsonSchema = translateSchemaObject(document, schema); - } catch { - // happens - } - } + const resolvedMediaObject = resolveMediaObject(this.document, mediaObject); + const { schema, encoding, examples } = resolvedMediaObject; + + const jsonSchema = translateSchemaMediaTypeObject.call(this, schema); const example = resolvedMediaObject.example || jsonSchema?.examples?.[0]; return { mediaType, - schema: jsonSchema, // Note that I'm assuming all references are resolved - examples: compact( - union( - example ? [{ key: 'default', value: example }] : undefined, - isDictionary(examples) - ? Object.keys(examples).map(exampleKey => ({ - key: exampleKey, - summary: get(examples, [exampleKey, 'summary']), - description: get(examples, [exampleKey, 'description']), - value: get(examples, [exampleKey, 'value']), - })) - : [], - ), + examples: [ + example ? transformDefaultExample.call(this, 'default', example) : undefined, + ...entries(examples).map(translateToExample, this), + ].filter(isNonNullable), + encodings: entries(encoding).map(translateEncodingPropertyObject, this).filter(isNonNullable), + + ...pickBy( + { + schema: jsonSchema, + }, + isNonNullable, ), - encodings: map(encoding, translateEncodingPropertyObject), }; -} +}; -function resolveMediaObject(document: DeepPartial, maybeMediaObject: Dictionary) { +function resolveMediaObject(document: unknown, maybeMediaObject: Dictionary) { const mediaObject = { ...maybeMediaObject }; - if (isDictionary(mediaObject.schema)) { + if (isPlainObject(mediaObject.schema)) { mediaObject.schema = maybeResolveLocalRef(document, mediaObject.schema); } - if (isDictionary(mediaObject.examples)) { + if (isPlainObject(mediaObject.examples)) { const examples = { ...mediaObject.examples }; mediaObject.examples = examples; - each(examples, (exampleValue, exampleName) => { + for (const [exampleName, exampleValue] of entries(examples)) { examples[exampleName] = maybeResolveLocalRef(document, exampleValue); - }); + } } return mediaObject; } -const transformExamples = - (source: MediaTypeObject | HeaderObject) => - (key: string): INodeExample => { - return { - summary: get(source, ['examples', key, 'summary']), - description: get(source, ['examples', key, 'description']), - value: get(source, ['examples', key, 'value']), - key, - }; +const transformDefaultExample: Oas3TranslateFunction<[key: string, value: unknown], INodeExample> = function ( + key, + value, +) { + return { + value, + key, + }; +}; + +const translateToExample: Oas3TranslateFunction< + ArrayCallbackParameters<[key: string, example: unknown]>, + Optional +> = function ([key, example]) { + if (!isPlainObject(example)) return; + + return { + value: example.value, + key, + + ...pickBy( + { + summary: example.summary, + description: example.description, + }, + isString, + ), }; +}; diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index b0e99d27..f7997316 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -1,107 +1,143 @@ +import { isPlainObject } from '@stoplight/json'; import type { - DeepPartial, - Dictionary, - IHttpCookieParam, IHttpHeaderParam, IHttpOperationRequest, IHttpOperationRequestBody, IHttpParam, - IHttpPathParam, - IHttpQueryParam, - IMediaTypeContent, + INodeExample, + INodeExternalExample, Optional, } from '@stoplight/types'; -import { compact, map, omit, partial, pickBy } from 'lodash'; -import type { OpenAPIObject, ParameterObject, RequestBodyObject } from 'openapi3-ts'; +import { HttpParamStyles } from '@stoplight/types'; +import type { JSONSchema7 } from 'json-schema'; +import type { ParameterObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import { isNonNullable, isString } from '../../guards'; +import { OasVersion } from '../../oas'; +import { getValidOasParameters } from '../../oas/accessors'; +import { isValidParamStyle } from '../../oas/guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import type { ArrayCallbackParameters, Fragment } from '../../types'; +import { entries } from '../../utils'; import { isRequestBodyObject } from '../guards'; +import type { Oas3TranslateFunction } from '../types'; import { translateMediaTypeObject } from './content'; -function translateRequestBody( - document: DeepPartial, - requestBodyObject: RequestBodyObject, -): IHttpOperationRequestBody { +export const translateRequestBody: Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> = + function (requestBodyObject) { + const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); + + if (isRequestBodyObject(resolvedRequestBodyObject)) { + return { + contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), + + ...pickBy( + { + required: resolvedRequestBodyObject.required, + description: resolvedRequestBodyObject.description, + }, + isNonNullable, + ), + }; + } + + return { contents: [] }; + }; + +const translateParameterObjectSchema: Oas3TranslateFunction< + [parameterObject: Fragment], + Optional +> = function (parameterObject) { + if (!isPlainObject(parameterObject.schema)) return; + + return translateSchemaObject.call(this, { + ...parameterObject.schema, + ...('example' in parameterObject ? { example: parameterObject.example } : null), + }); +}; + +const translateToExample: Oas3TranslateFunction< + ArrayCallbackParameters<[key: string, example: unknown]>, + Optional +> = function ([key, example]) { + if (!isPlainObject(example)) return; + + if (!('value' in example) && typeof example.externalValue !== 'string') return; + return { - required: requestBodyObject.required, - description: requestBodyObject.description, - contents: compact( - map & unknown, Optional>( - requestBodyObject.content, - partial(translateMediaTypeObject, document), - ), + key, + + ...(typeof example.externalValue === 'string' + ? { externalValue: example.externalValue } + : { value: example.value }), + + ...pickBy( + { + summary: example.summary, + description: example.description, + }, + isString, ), }; -} +}; -export function translateParameterObject( - document: DeepPartial, - parameterObject: ParameterObject, -): IHttpParam | any { - const examples = map(parameterObject.examples, (example, key) => ({ - key, - ...example, - })); - - const hasDefaultExample = examples.map(({ key }) => key).includes('default'); - - return pickBy({ - ...omit(parameterObject, 'in', 'schema', 'example'), - name: parameterObject.name, - style: parameterObject.style, - schema: isDictionary(parameterObject.schema) - ? translateSchemaObject(document, { - ...parameterObject.schema, - ...('example' in parameterObject ? { example: parameterObject.example } : null), - }) - : void 0, - examples: - 'example' in parameterObject && !hasDefaultExample - ? [{ key: 'default', value: parameterObject.example }, ...examples] - : examples, - }); -} - -export function translateToRequest( - document: DeepPartial, - parameters: ParameterObject[], - requestBodyObject?: RequestBodyObject, -): IHttpOperationRequest { - const params: { - header: IHttpHeaderParam[]; - query: IHttpQueryParam[]; - cookie: IHttpCookieParam[]; - path: IHttpPathParam[]; - } = { - header: [], - query: [], - cookie: [], - path: [], +export const translateParameterObject: Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> = + function (parameterObject) { + const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); + + const hasDefaultExample = examples.some(({ key }) => key.includes('default')); + const schema = translateParameterObjectSchema.call(this, parameterObject); + + return { + name: parameterObject.name, + deprecated: !!parameterObject.deprecated, + style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, + explode: !!(parameterObject.explode ?? parameterObject.style === HttpParamStyles.Form), + examples: + 'example' in parameterObject && !hasDefaultExample + ? [{ key: 'default', value: parameterObject.example }, ...examples] + : examples, + + ...pickBy( + { + description: parameterObject.description, + }, + isString, + ), + + ...pickBy( + { + schema, + content: parameterObject.content, + }, + isPlainObject, + ), + }; }; - for (const parameter of parameters) { - const { in: key } = parameter; - if (!params.hasOwnProperty(key)) continue; +export const translateToRequest: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IHttpOperationRequest> = + function (path, operation) { + const params: Omit & { header: IHttpHeaderParam[] } = { + header: [], + query: [], + cookie: [], + path: [], + }; - params[key].push(translateParameterObject(document, parameter)); - } + for (const param of getValidOasParameters(this.document, OasVersion.OAS3, operation.parameters, path.parameters)) { + const { in: key } = param; + const target = params[key]; + if (!Array.isArray(target)) continue; - let body; - if (isDictionary(requestBodyObject)) { - const resolvedRequestBodyObject = maybeResolveLocalRef(document, requestBodyObject) as RequestBodyObject; - body = isRequestBodyObject(resolvedRequestBodyObject) - ? translateRequestBody(document, resolvedRequestBodyObject) - : { contents: [] }; - } else { - body = { contents: [] }; - } + target.push(translateParameterObject.call(this, param) as any); + } - return { - body, - headers: params.header, - query: params.query, - cookie: params.cookie, - path: params.path, + return { + body: translateRequestBody.call(this, operation?.requestBody), + headers: params.header, + query: params.query, + cookie: params.cookie, + path: params.path, + }; }; -} diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index 4a2e8742..2f7225f9 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,56 +1,36 @@ -import { - DeepPartial, - Dictionary, - IHttpHeaderParam, - IHttpOperationResponse, - IMediaTypeContent, - Optional, -} from '@stoplight/types'; -import { compact, map, partial, reduce } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; +import type { IHttpOperationResponse, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import { isNonNullable, isString } from '../../guards'; +import type { ArrayCallbackParameters } from '../../types'; +import { entries } from '../../utils'; import { isResponseObject } from '../guards'; +import type { Oas3TranslateFunction } from '../types'; import { translateHeaderObject, translateMediaTypeObject } from './content'; -function translateToResponse( - document: DeepPartial, - response: unknown, - statusCode: string, -): Optional { - const resolvedResponse = maybeResolveLocalRef(document, response); +const translateToResponse: Oas3TranslateFunction< + ArrayCallbackParameters<[statusCode: string, response: unknown]>, + Optional +> = function ([statusCode, response]) { + const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; - const dereferencedHeaders = reduce( - resolvedResponse.headers, - (result, header, name) => { - return { ...result, [name]: maybeResolveLocalRef(document, header) }; - }, - {}, - ); - return { code: statusCode, - description: resolvedResponse.description, - headers: compact(map(dereferencedHeaders, translateHeaderObject)), - contents: compact( - map & unknown, Optional>( - resolvedResponse.content, - partial(translateMediaTypeObject, document), - ), + headers: entries(resolvedResponse.headers).map(translateHeaderObject, this).filter(isNonNullable), + contents: entries(resolvedResponse.content).map(translateMediaTypeObject, this).filter(isNonNullable), + + ...pickBy( + { + description: resolvedResponse.description, + }, + isString, ), }; -} - -export function translateToResponses( - document: DeepPartial, - responses: unknown, -): IHttpOperationResponse[] { - if (!isDictionary(responses)) { - return []; - } +}; - return compact( - map, Optional>(responses, partial(translateToResponse, document)), - ); -} +export const translateToResponses: Oas3TranslateFunction<[responses: unknown], IHttpOperationResponse[]> = function ( + responses, +) { + return entries(responses).map(translateToResponse, this).filter(isNonNullable); +}; diff --git a/src/oas3/transformers/securities.ts b/src/oas3/transformers/securities.ts index 2f5d45e2..53618e93 100644 --- a/src/oas3/transformers/securities.ts +++ b/src/oas3/transformers/securities.ts @@ -1,29 +1,26 @@ -import { DeepPartial, IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; -import { OpenAPIObject, SecuritySchemeObject } from 'openapi3-ts'; +import { isPlainObject } from '@stoplight/json'; +import type { IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; +import type { SecuritySchemeObject } from 'openapi3-ts'; -import { isDictionary } from '../../utils'; -import { getSecurities, OperationSecurities, SecurityWithKey } from '../accessors'; +import { isNonNullable } from '../../guards'; +import { ArrayCallbackParameters } from '../../types'; +import { getSecurities, SecurityWithKey } from '../accessors'; import { isOAuthFlowObject } from '../guards'; +import { Oas3TranslateFunction } from '../types'; -export function translateToSecurities(document: DeepPartial, operationSecurities: OperationSecurities) { - const securities = getSecurities(document, operationSecurities); +export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = + function (operationSecurities) { + const securities = getSecurities(this.document, operationSecurities); - return securities.map(security => - security.reduce((transformedSecurities, sec) => { - const transformed = transformToSingleSecurity(sec, sec.key); - if (transformed) { - transformedSecurities.push(transformed); - } + return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); + }; - return transformedSecurities; - }, []), - ); -} +export const translateToSingleSecurity: Oas3TranslateFunction< + [ArrayCallbackParameters & { type: 'mutualTLS' })>[0]], + Optional +> = function (securityScheme) { + const { key } = securityScheme; -export function transformToSingleSecurity( - securityScheme: SecuritySchemeObject | (Omit & { type: 'mutualTLS' }), - key: string, -): SecurityWithKey | undefined { const baseObject: { key: string; description?: string } = { key, }; @@ -82,12 +79,12 @@ export function transformToSingleSecurity( } return undefined; -} +}; function transformFlows(flows: Optional): IOauthFlowObjects { const transformedFlows: IOauthFlowObjects = {}; - if (!isDictionary(flows)) { + if (!isPlainObject(flows)) { return transformedFlows; } diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index ae0963c1..9a22e861 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,24 +1,89 @@ -import type { DeepPartial, Dictionary, INodeVariable, IServer } from '@stoplight/types'; -import { map, mapValues, pickBy } from 'lodash'; -import { ServerObject, ServerVariableObject } from 'openapi3-ts'; +import type { INodeVariable, IServer, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); -import { isServerVariableObject } from '../guards'; +import { isNonNullable, isString } from '../../guards'; +import { ArrayCallbackParameters, Fragment } from '../../types'; +import { entries } from '../../utils'; +import { isServerObject, isServerVariableObject } from '../guards'; +import { Oas3TranslateFunction } from '../types'; -export function translateToServers(servers: ServerObject[]): IServer[] { - return servers.map(server => ({ - description: server.description, +export const translateToServers: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IServer[]> = function ( + path, + operation, +) { + let servers; + if (Array.isArray(operation.servers)) { + servers = operation.servers; + } else if (Array.isArray(path.servers)) { + servers = path.servers; + } else if (Array.isArray(this.document.servers)) { + servers = this.document.servers; + } else { + return []; + } + + return servers.map(translateToServer, this).filter(isNonNullable); +}; + +export const translateToServer: Oas3TranslateFunction, Optional> = function ( + server, +) { + if (!isServerObject(server)) return; + + const variables = translateServerVariables.call(this, server.variables); + + return { url: server.url, - variables: server.variables && translateServerVariables(server.variables), - })); -} - -export function translateServerVariables(variables: DeepPartial<{ [v: string]: ServerVariableObject }>) { - return mapValues, INodeVariable>( - pickBy(variables, isServerVariableObject), - value => ({ - default: String(value.default), - description: value.description && String(value.description), - enum: value.enum && map(value.enum, String), - }), - ); -} + + ...pickBy( + { + name: this.document.info?.title, + description: server.description, + }, + isString, + ), + + ...pickBy( + { + variables, + }, + isNonNullable, + ), + }; +}; + +export const translateServerVariables: Oas3TranslateFunction< + [variables: unknown], + Optional> +> = variables => { + const serverVariables = entries(variables).map(translateServerVariable).filter(isNonNullable); + return serverVariables.length > 0 ? Object.fromEntries(serverVariables) : undefined; +}; + +const translateServerVariable: Oas3TranslateFunction< + ArrayCallbackParameters<[name: string, variable: unknown]>, + Optional<[string, INodeVariable]> +> = function ([name, variable]) { + if (!isServerVariableObject(variable)) return; + + return [ + name, + { + default: String(variable.default), + + ...pickBy( + { + description: variable.description, + }, + isString, + ), + + ...pickBy( + { + enum: Array.isArray(variable.enum) ? variable.enum.map(String) : undefined, + }, + isNonNullable, + ), + }, + ]; +}; diff --git a/src/oas3/types.ts b/src/oas3/types.ts new file mode 100644 index 00000000..c452b6b3 --- /dev/null +++ b/src/oas3/types.ts @@ -0,0 +1,10 @@ +import { DeepPartial } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; + +import { TranslateFunction } from '../types'; + +export type Oas3TranslateFunction

= TranslateFunction< + DeepPartial, + P, + R +>; diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index 3efb81d4..fd51a78c 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -255,7 +255,7 @@ describe('transformBody()', () => { ); // verify shape of example object - expect(Object.entries((result!.contents![0].examples![0] as INodeExample).value)).toEqual( + expect(Object.entries((result!.contents![0].examples![0] as INodeExample).value as unknown[])).toEqual( expect.arrayContaining([ [expect.stringMatching(/^_gen_[0-9a-f]{6}$/), 'v1'], [expect.stringMatching(/^_gen_[0-9a-f]{6}$/), 'v2'], @@ -264,7 +264,7 @@ describe('transformBody()', () => { // ensure both share exactly the same keys expect(Object.keys(result!.contents![0].schema!.properties!).sort()).toEqual( - Object.keys((result!.contents![0].examples![0] as INodeExample).value).sort(), + Object.keys((result!.contents![0].examples![0] as INodeExample).value as unknown[]).sort(), ); }); }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 8ab4f976..341e8fa4 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -6,13 +6,13 @@ import { IHttpQueryParam, IMediaTypeContent, } from '@stoplight/types'; -import { JSONSchema7 } from 'json-schema'; +import type { JSONSchema7 } from 'json-schema'; // @ts-ignore import * as jsonSchemaGenerator from 'json-schema-generator'; import { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; -import { translateSchemaObject } from '../../oas/transformers/schema'; +import { convertSchema } from '../../oas/transformers/schema'; import { transformDescriptionDefinition, transformStringValueToSchema } from '../util'; export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { @@ -82,7 +82,7 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): value: parsed, }, ], - schema: translateSchemaObject({}, jsonSchemaGenerator(parsed)), + schema: convertSchema(jsonSchemaGenerator(parsed)), }; } catch (e) { /* noop, move on.. */ @@ -104,7 +104,7 @@ function transformParamsBody( params: PropertyList, mediaType: string, ): IMediaTypeContent { - const paramsList: { name: string; schema: JSONSchema7; value: any }[] = params.map(item => { + const paramsList: { name: string; schema: JSONSchema7; value: unknown }[] = params.map(item => { return { name: item.key || generateId(), schema: { diff --git a/src/postman/transformers/securityScheme.ts b/src/postman/transformers/securityScheme.ts index ecc087e4..816b2cb3 100644 --- a/src/postman/transformers/securityScheme.ts +++ b/src/postman/transformers/securityScheme.ts @@ -1,6 +1,6 @@ import { HttpParamStyles, HttpSecurityScheme, IHttpHeaderParam, IHttpQueryParam } from '@stoplight/types'; -import { isEqual, omit } from 'lodash'; import { Collection, RequestAuth } from 'postman-collection'; +import isEqual = require('lodash.isequal'); export type PostmanSecurityScheme = StandardSecurityScheme | QuerySecurityScheme | HeaderSecurityScheme; @@ -227,7 +227,9 @@ export function isPostmanSecuritySchemeEqual(pss1: PostmanSecurityScheme, pss2: if (pss1.type !== pss2.type) return false; if (isStandardSecurityScheme(pss1) && isStandardSecurityScheme(pss2)) { - return isEqual(omit(pss1.securityScheme, 'key'), omit(pss2.securityScheme, 'key')); + const { key: _key, ...pss1SecurityScheme } = pss1.securityScheme; + const { key: _key2, ...pss2SecurityScheme } = pss2.securityScheme; + return isEqual(pss1SecurityScheme, pss2SecurityScheme); } return isEqual(pss1, pss2); diff --git a/src/types.ts b/src/types.ts index f4552e2a..5ec95eff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,15 +1,29 @@ -import { DeepPartial, IHttpOperation, IHttpService } from '@stoplight/types'; +import type { IHttpOperation, IHttpService } from '@stoplight/types'; -export interface ITransformServiceOpts { - document: DeepPartial; +export type Fragment = Record; + +export interface ITransformServiceOpts { + document: T; } export type HttpServiceTransformer = (opts: T) => IHttpService; -export interface ITransformOperationOpts { - document: DeepPartial; +export interface ITransformOperationOpts { + document: T; path: string; method: string; } export type HttpOperationTransformer = (opts: T) => IHttpOperation; + +export type TransformerContext = { + maybeResolveLocalRef(target: unknown): unknown; + document: T; +}; + +export type TranslateFunction = ( + this: TransformerContext, + ...params: P +) => R; + +export type ArrayCallbackParameters = [T, number, T[]]; diff --git a/src/utils.ts b/src/utils.ts index 4b0d5ea0..3d177946 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,7 @@ -import { hasRef, isLocalRef, resolveInlineRef } from '@stoplight/json'; -import { Dictionary, Optional } from '@stoplight/types'; -import { isObjectLike, map } from 'lodash'; - -export function mapToKeys(collection: Optional) { - return map(collection, Object.keys); -} - -export const isDictionary = (maybeDictionary: unknown): maybeDictionary is Dictionary => - isObjectLike(maybeDictionary); +import { hasRef, isLocalRef, isPlainObject, resolveInlineRef } from '@stoplight/json'; export const maybeResolveLocalRef = (document: unknown, target: unknown): unknown => { - if (isDictionary(document) && hasRef(target) && isLocalRef(target.$ref)) { + if (isPlainObject(document) && hasRef(target) && isLocalRef(target.$ref)) { try { return resolveInlineRef(document, target.$ref); } catch { @@ -20,3 +11,11 @@ export const maybeResolveLocalRef = (document: unknown, target: unknown): unknow return target; }; + +export { isPlainObject as isDictionary }; + +export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; +export function entries(o: T): [string, T][]; +export function entries(o: T): [string, T][] { + return isPlainObject(o) ? Object.entries(o as T) : []; +} diff --git a/yarn.lock b/yarn.lock index b0e76aba..88d5e7d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,7 +1081,7 @@ resolved "https://registry.yarnpkg.com/@stoplight/test-utils/-/test-utils-0.0.1.tgz#b0d38c8a0abebda2dacbc2aa6a4f9bec44878e7b" integrity sha512-Cj3waLFR9bYLG8yvgkjXMxOfiVdewRyrKdH5RQpttVNfKj5UF+mElofPcHn/UfiOuxK3t5rbsdMfS26LK5tdQg== -"@stoplight/types@^12.3.0", "@stoplight/types@^12.4.0": +"@stoplight/types@^12.3.0": version "12.4.0" resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.4.0.tgz#7a90e747d3f531742316b7b2969e6c45efd3e968" integrity sha512-ELf0dOdROaKEE0iw1YF9qbL9QjIVfdyiCoBPpUQmvQej9F7tQR/TsYMvyUb8kz+rd49u3hGP/sirVuaTKI1ZOg== @@ -1089,6 +1089,14 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" +"@stoplight/types@^13.0.0-beta.4": + version "13.0.0-beta.4" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.4.tgz#5ea5e93957b20a5effedcbd101e6e9cde7118e48" + integrity sha512-q9Dbyfi1DcZjTuDwptVLTCbeQgtpfTQAKQkbcrs64ayfvc7dPwO+81vYr0dSJsXzlFQktZv5M+eq0QPtJy/HGg== + dependencies: + "@types/json-schema" "^7.0.4" + utility-types "^3.10.0" + "@tootallnate/once@1": version "1.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" @@ -1192,10 +1200,31 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash@4.14.157": - version "4.14.157" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.157.tgz#fdac1c52448861dfde1a2e1515dbc46e54926dc8" - integrity sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ== +"@types/lodash.isequal@^4.5.5": + version "4.5.5" + resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz#4fed1b1b00bef79e305de0352d797e9bb816c8ff" + integrity sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.pick@^4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/lodash.pick/-/lodash.pick-4.4.6.tgz#ae4e8f109e982786313bb6aac4b1a73aefa6e9be" + integrity sha512-u8bzA16qQ+8dY280z3aK7PoWb3fzX5ATJ0rJB6F+uqchOX2VYF02Aqa+8aYiHiHgPzQiITqCgeimlyKFy4OA6g== + dependencies: + "@types/lodash" "*" + +"@types/lodash.pickby@^4.6.6": + version "4.6.6" + resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz#3dc39c2b38432f7a0c5e5627b0d5c0e3878b4f35" + integrity sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.180" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" + integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== "@types/node@*", "@types/node@>= 8": version "13.9.1" @@ -1253,11 +1282,6 @@ dependencies: "@types/node" "*" -"@types/urijs@~1.19.9": - version "1.19.17" - resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.17.tgz#232ac9884b6a2aeab5dbe70b79cdb91d5067c325" - integrity sha512-ShIlp+8iNGo/yVVfYFoNRqUiaE9wMCzsSl85qTg2/C5l56BTJokU7QeMgVBQ9xhcyhWQP0zGXPBZPPvEG/sRmQ== - "@types/yargs-parser@*": version "13.1.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" @@ -5358,11 +5382,6 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-schema@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -5807,6 +5826,11 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -5837,6 +5861,16 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.pickby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" + integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8= + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" From 6c96a068efd2e2dc7b381e4fb5f25e6702cddc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Fri, 18 Mar 2022 01:50:03 +0100 Subject: [PATCH 06/16] feat: stable ids --- jest.config.js | 1 + package.json | 8 +- src/__tests__/merge.test.ts | 244 ++++--- src/__tests__/utils.test.ts | 37 - src/context.ts | 66 +- src/merge.ts | 5 +- .../__snapshots__/operation.test.ts.snap | 323 ++++++++- .../__snapshots__/service.test.ts.snap | 20 +- src/oas/__tests__/accessors.test.ts | 182 +++-- .../__tests__/fixtures/oas2-kitchen-sink.json | 1 + .../__tests__/fixtures/oas3-kitchen-sink.json | 1 + src/oas/__tests__/service.test.ts | 2 +- src/oas/__tests__/tags.test.ts | 13 +- src/oas/accessors.ts | 68 +- src/oas/operation.ts | 55 +- src/oas/resolveInlineRef.ts | 57 ++ src/oas/resolver.ts | 32 + src/oas/service.ts | 5 +- src/oas/tags.ts | 23 +- src/oas/transformers/examples.ts | 18 + .../schema/__tests__/schema.spec.ts | 16 +- src/oas/transformers/schema/index.ts | 42 +- .../__snapshots__/operation.test.ts.snap | 11 +- src/oas2/__tests__/guards.test.ts | 39 -- src/oas2/__tests__/operation.test.ts | 45 +- src/oas2/__tests__/service.test.ts | 27 +- src/oas2/accessors.ts | 8 +- src/oas2/guards.ts | 8 + src/oas2/index.ts | 1 - src/oas2/operation.ts | 51 +- src/oas2/service.ts | 5 +- .../__snapshots__/params.test.ts.snap | 18 + .../__snapshots__/responses.test.ts.snap | 228 +++---- .../transformers/__tests__/params.test.ts | 75 ++- .../transformers/__tests__/request.test.ts | 5 +- .../transformers/__tests__/responses.test.ts | 146 +++- .../transformers/__tests__/securities.test.ts | 19 +- .../transformers/__tests__/servers.test.ts | 15 +- src/oas2/transformers/params.ts | 135 ++-- src/oas2/transformers/request.ts | 38 +- src/oas2/transformers/responses.ts | 51 +- src/oas2/transformers/securities.ts | 87 ++- src/oas2/transformers/servers.ts | 46 +- src/oas3/__fixtures__/id.json | 214 ++++++ src/oas3/__fixtures__/output.ts | 489 ++++++++++++++ .../__snapshots__/operation.test.ts.snap | 36 +- src/oas3/__tests__/accessors.test.ts | 96 +-- src/oas3/__tests__/ids.test.ts | 12 + src/oas3/__tests__/operation.test.ts | 193 +++++- src/oas3/__tests__/service.test.ts | 39 +- src/oas3/accessors.ts | 46 +- src/oas3/guards.ts | 5 - src/oas3/index.ts | 1 - src/oas3/operation.ts | 54 +- src/oas3/service.ts | 30 +- .../__snapshots__/content.test.ts.snap | 23 + .../__snapshots__/request.test.ts.snap | 20 +- .../__snapshots__/responses.test.ts.snap | 165 ++--- .../__snapshots__/servers.test.ts.snap | 1 + .../transformers/__tests__/content.test.ts | 120 +++- .../transformers/__tests__/request.test.ts | 32 +- .../transformers/__tests__/responses.test.ts | 135 +++- .../transformers/__tests__/securities.test.ts | 21 +- .../transformers/__tests__/servers.test.ts | 13 +- src/oas3/transformers/content.ts | 114 +--- src/oas3/transformers/examples.ts | 33 + src/oas3/transformers/request.ts | 187 +++--- src/oas3/transformers/responses.ts | 21 +- src/oas3/transformers/securities.ts | 27 +- src/oas3/transformers/servers.ts | 23 +- src/postman/__tests__/operation.test.ts | 36 +- src/postman/__tests__/service.test.ts | 1 + src/postman/__tests__/util.test.ts | 2 +- src/postman/id.ts | 8 + src/postman/operation.ts | 6 +- src/postman/service.ts | 4 +- .../transformers/__tests__/params.test.ts | 63 +- .../transformers/__tests__/request.test.ts | 12 +- .../transformers/__tests__/response.test.ts | 24 + .../__tests__/securitySchemes.spec.ts | 79 ++- .../transformers/__tests__/server.test.ts | 10 +- src/postman/transformers/params.ts | 25 +- src/postman/transformers/response.ts | 4 + src/postman/transformers/securityScheme.ts | 43 +- src/postman/transformers/server.ts | 8 +- src/postman/types.ts | 4 +- src/postman/util.ts | 5 +- src/track.ts | 0 src/types.ts | 19 +- src/utils.ts | 24 +- yarn.lock | 633 ++++++++++++++---- 91 files changed, 3955 insertions(+), 1482 deletions(-) delete mode 100644 src/__tests__/utils.test.ts create mode 100644 src/oas/resolveInlineRef.ts create mode 100644 src/oas/resolver.ts create mode 100644 src/oas/transformers/examples.ts delete mode 100644 src/oas2/__tests__/guards.test.ts create mode 100644 src/oas3/__fixtures__/id.json create mode 100644 src/oas3/__fixtures__/output.ts create mode 100644 src/oas3/__tests__/ids.test.ts create mode 100644 src/oas3/transformers/examples.ts create mode 100644 src/postman/id.ts create mode 100644 src/track.ts diff --git a/jest.config.js b/jest.config.js index 4a5b465e..a9426838 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,5 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', + testMatch: ['/src/**/__tests__/*.(spec|test).(ts|js)'], }; diff --git a/package.json b/package.json index 6d059d7e..89c0da7c 100644 --- a/package.json +++ b/package.json @@ -53,12 +53,13 @@ }, "dependencies": { "@stoplight/json": "^3.17.2", - "@stoplight/types": "^13.0.0-beta.4", + "@stoplight/types": "^13.0.0-beta.5", "@types/json-schema": "7.0.5", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", + "fnv-plus": "^1.3.1", "json-schema-generator": "^2.0.6", - "lodash.isequal": "^4.5.0", + "lodash.isequalwith": "^4.4.0", "lodash.pick": "^4.4.0", "lodash.pickby": "^4.6.0", "openapi3-ts": "^2.0.1", @@ -71,9 +72,10 @@ "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", - "@types/lodash.isequal": "^4.5.5", + "@types/lodash.isequalwith": "^4.4.6", "@types/lodash.pick": "^4.4.6", "@types/lodash.pickby": "^4.6.6", + "@types/memoizee": "^0.4.7", "@types/postman-collection": "^3.5.3", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", diff --git a/src/__tests__/merge.test.ts b/src/__tests__/merge.test.ts index 2c21375c..031823ca 100644 --- a/src/__tests__/merge.test.ts +++ b/src/__tests__/merge.test.ts @@ -8,59 +8,66 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', headers: [ - { name: '200h1', style: HttpParamStyles.Simple, required: true }, - { name: '200h2', style: HttpParamStyles.Simple, required: true }, + { id: 'b', name: '200h1', style: HttpParamStyles.Simple, required: true }, + { id: 'c', name: '200h2', style: HttpParamStyles.Simple, required: true }, ], }, { + id: 'd', code: '400', headers: [ - { name: '400h1', style: HttpParamStyles.Simple, required: true }, - { name: '400h2', style: HttpParamStyles.Simple, required: true }, + { id: 'e', name: '400h1', style: HttpParamStyles.Simple, required: true }, + { id: 'f', name: '400h2', style: HttpParamStyles.Simple, required: true }, ], }, ], [ { + id: 'g', code: '200', headers: [ - { name: '200h2', style: HttpParamStyles.Simple, required: true }, - { name: '200h3', style: HttpParamStyles.Simple, required: true }, + { id: 'h', name: '200h2', style: HttpParamStyles.Simple, required: true }, + { id: 'i', name: '200h3', style: HttpParamStyles.Simple, required: true }, ], }, { + id: 'j', code: '500', headers: [ - { name: '500h1', style: HttpParamStyles.Simple, required: true }, - { name: '500h2', style: HttpParamStyles.Simple, required: true }, + { id: 'k', name: '500h1', style: HttpParamStyles.Simple, required: true }, + { id: 'l', name: '500h2', style: HttpParamStyles.Simple, required: true }, ], }, ], ), ).toEqual([ { + id: 'a', code: '200', headers: [ - { name: '200h1', style: HttpParamStyles.Simple, required: false }, - { name: '200h2', style: HttpParamStyles.Simple, required: true }, - { name: '200h3', style: HttpParamStyles.Simple, required: false }, + { id: 'b', name: '200h1', style: HttpParamStyles.Simple, required: false }, + { id: 'c', name: '200h2', style: HttpParamStyles.Simple, required: true }, + { id: 'i', name: '200h3', style: HttpParamStyles.Simple, required: false }, ], contents: [], }, { + id: 'd', code: '400', headers: [ - { name: '400h1', style: HttpParamStyles.Simple, required: true }, - { name: '400h2', style: HttpParamStyles.Simple, required: true }, + { id: 'e', name: '400h1', style: HttpParamStyles.Simple, required: true }, + { id: 'f', name: '400h2', style: HttpParamStyles.Simple, required: true }, ], }, { + id: 'j', code: '500', headers: [ - { name: '500h1', style: HttpParamStyles.Simple, required: true }, - { name: '500h2', style: HttpParamStyles.Simple, required: true }, + { id: 'k', name: '500h1', style: HttpParamStyles.Simple, required: true }, + { id: 'l', name: '500h2', style: HttpParamStyles.Simple, required: true }, ], }, ]); @@ -71,45 +78,50 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, - { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'b', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'c', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'd', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, ], [ { + id: 'e', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, - { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'string' } }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'f', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'g', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'string' } }, + { id: 'h', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, { + id: 'i', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, - { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'boolean' } }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'j', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'k', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'boolean' } }, + { id: 'l', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, ], ), ).toEqual([ { + id: 'a', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'b', name: 'h1', style: HttpParamStyles.Simple, required: true }, { + id: 'c', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { anyOf: [{ type: 'number' }, { type: 'string' }, { type: 'boolean' }] }, }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'd', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], contents: [], }, @@ -121,21 +133,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - headers: [{ name: 'h', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'b', name: 'h', style: HttpParamStyles.Simple, required: true }], }, ], [ { + id: 'c', code: '200', - headers: [{ name: 'h', style: HttpParamStyles.Simple, required: false }], + headers: [{ id: 'd', name: 'h', style: HttpParamStyles.Simple, required: false }], }, ], ), ).toEqual([ { + id: 'a', code: '200', - headers: [{ name: 'h', style: HttpParamStyles.Simple, required: false }], + headers: [{ id: 'b', name: 'h', style: HttpParamStyles.Simple, required: false }], contents: [], }, ]); @@ -146,21 +161,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - headers: [{ name: 'Oo', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'b', name: 'Oo', style: HttpParamStyles.Simple, required: true }], }, ], [ { + id: 'c', code: '200', - headers: [{ name: 'oO', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'd', name: 'oO', style: HttpParamStyles.Simple, required: true }], }, ], ), ).toEqual([ { + id: 'a', code: '200', - headers: [{ name: 'Oo', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'b', name: 'Oo', style: HttpParamStyles.Simple, required: true }], contents: [], }, ]); @@ -171,38 +189,67 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - contents: [{ mediaType: '200-tion/a-son' }, { mediaType: '200-tion/b-son' }], + contents: [ + { id: 'a1', mediaType: '200-tion/a-son' }, + { id: 'a2', mediaType: '200-tion/b-son' }, + ], }, { + id: 'b', code: '400', - contents: [{ mediaType: '400-tion/a-son' }, { mediaType: '400-tion/b-son' }], + contents: [ + { id: 'b1', mediaType: '400-tion/a-son' }, + { id: 'b2', mediaType: '400-tion/b-son' }, + ], }, ], [ { + id: 'c', code: '200', - contents: [{ mediaType: '200-tion/b-son' }, { mediaType: '200-tion/c-son' }], + contents: [ + { id: 'c1', mediaType: '200-tion/b-son' }, + { id: 'c2', mediaType: '200-tion/c-son' }, + ], }, { + id: 'd', code: '500', - contents: [{ mediaType: '500-tion/a-son' }, { mediaType: '500-tion/b-son' }], + contents: [ + { id: 'd1', mediaType: '500-tion/a-son' }, + { id: 'd2', mediaType: '500-tion/b-son' }, + ], }, ], ), ).toEqual([ { + id: 'a', code: '200', - contents: [{ mediaType: '200-tion/a-son' }, { mediaType: '200-tion/b-son' }, { mediaType: '200-tion/c-son' }], + contents: [ + { id: 'a1', mediaType: '200-tion/a-son' }, + { id: 'a2', mediaType: '200-tion/b-son' }, + { id: 'c2', mediaType: '200-tion/c-son' }, + ], headers: [], }, { + id: 'b', code: '400', - contents: [{ mediaType: '400-tion/a-son' }, { mediaType: '400-tion/b-son' }], + contents: [ + { id: 'b1', mediaType: '400-tion/a-son' }, + { id: 'b2', mediaType: '400-tion/b-son' }, + ], }, { + id: 'd', code: '500', - contents: [{ mediaType: '500-tion/a-son' }, { mediaType: '500-tion/b-son' }], + contents: [ + { id: 'd1', mediaType: '500-tion/a-son' }, + { id: 'd2', mediaType: '500-tion/b-son' }, + ], }, ]); }); @@ -212,17 +259,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', contents: [ - { mediaType: 'application/json', schema: { type: 'object', properties: { a: { type: 'string' } } } }, + { + id: 'a1', + mediaType: 'application/json', + schema: { type: 'object', properties: { a: { type: 'string' } } }, + }, ], }, ], [ { + id: 'b', code: '200', contents: [ { + id: 'b1', mediaType: 'application/json', schema: { type: 'object', properties: { a: { type: 'string' }, b: { type: 'string' } } }, }, @@ -232,9 +286,11 @@ describe('mergeResponses()', () => { ), ).toEqual([ { + id: 'a', code: '200', contents: [ { + id: 'a1', mediaType: 'application/json', schema: { anyOf: [ @@ -254,21 +310,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - contents: [{ mediaType: 'Aa/Oo' }], + contents: [{ id: 'a1', mediaType: 'Aa/Oo' }], }, ], [ { + id: 'b', code: '200', - contents: [{ mediaType: 'aA/oO' }], + contents: [{ id: 'b1', mediaType: 'aA/oO' }], }, ], ), ).toEqual([ { + id: 'a', code: '200', - contents: [{ mediaType: 'Aa/Oo' }], + contents: [{ id: 'a1', mediaType: 'Aa/Oo' }], headers: [], }, ]); @@ -280,12 +339,28 @@ describe('mergeOperations()', () => { expect( mergeOperations( [ - { id: '1', method: 'get', path: '/a', responses: [{ code: '200' }, { code: '400' }] }, - { id: '2', method: 'get', path: '/b', responses: [{ code: '200' }] }, + { + id: '1', + method: 'get', + path: '/a', + responses: [ + { id: 'a', code: '200' }, + { id: 'b', code: '400' }, + ], + }, + { id: '2', method: 'get', path: '/b', responses: [{ id: 'c', code: '200' }] }, ], [ - { id: '3', method: 'get', path: '/a', responses: [{ code: '200' }, { code: '500' }] }, - { id: '4', method: 'get', path: '/c', responses: [{ code: '200' }] }, + { + id: '3', + method: 'get', + path: '/a', + responses: [ + { id: 'd', code: '200' }, + { id: 'e', code: '500' }, + ], + }, + { id: '4', method: 'get', path: '/c', responses: [{ id: 'f', code: '200' }] }, ], ), ).toEqual([ @@ -293,12 +368,16 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200', contents: [], headers: [] }, { code: '400' }, { code: '500' }], + responses: [ + { id: 'a', code: '200', contents: [], headers: [] }, + { id: 'b', code: '400' }, + { id: 'e', code: '500' }, + ], servers: [], request: {}, }, - { id: '2', method: 'get', path: '/b', responses: [{ code: '200' }] }, - { id: '4', method: 'get', path: '/c', responses: [{ code: '200' }] }, + { id: '2', method: 'get', path: '/b', responses: [{ id: 'c', code: '200' }] }, + { id: '4', method: 'get', path: '/c', responses: [{ id: 'f', code: '200' }] }, ]); }); @@ -310,8 +389,8 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200' }], - servers: [{ url: 'http://example.com' }], + responses: [{ id: 'a', code: '200' }], + servers: [{ id: 'b', url: 'http://example.com' }], }, ], [ @@ -319,8 +398,8 @@ describe('mergeOperations()', () => { id: '2', method: 'get', path: '/a', - responses: [{ code: '200' }], - servers: [{ url: 'https://example.com' }], + responses: [{ id: 'c', code: '200' }], + servers: [{ id: 'd', url: 'https://example.com' }], }, ], ), @@ -329,8 +408,11 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200', headers: [], contents: [] }], - servers: [{ url: 'http://example.com' }, { url: 'https://example.com' }], + responses: [{ id: 'a', code: '200', headers: [], contents: [] }], + servers: [ + { id: 'b', url: 'http://example.com' }, + { id: 'd', url: 'https://example.com' }, + ], request: {}, }, ]); @@ -344,22 +426,24 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200' }], + responses: [{ id: 'a', code: '200' }], request: { headers: [ - { name: 'a', style: HttpParamStyles.Simple, required: true }, - { name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'b', name: 'a', style: HttpParamStyles.Simple, required: true }, + { id: 'c', name: 'b', style: HttpParamStyles.Simple, required: true }, ], query: [ - { name: 'a', style: HttpParamStyles.Form, required: true }, - { name: 'b', style: HttpParamStyles.Form, required: true }, + { id: 'd', name: 'a', style: HttpParamStyles.Form, required: true }, + { id: 'e', name: 'b', style: HttpParamStyles.Form, required: true }, ], - path: [{ name: 'a', style: HttpParamStyles.Simple, required: true }], + path: [{ id: 'f', name: 'a', style: HttpParamStyles.Simple, required: true }], body: { + id: 'g', description: 'The cadillac stood by the house', required: true, contents: [ { + id: 'g1', mediaType: 'application/json', schema: { type: 'string' }, }, @@ -373,25 +457,27 @@ describe('mergeOperations()', () => { id: '2', method: 'get', path: '/a', - responses: [{ code: '200' }], + responses: [{ id: '11', code: '200' }], request: { headers: [ - { name: 'b', style: HttpParamStyles.Simple, required: true }, - { name: 'c', style: HttpParamStyles.Simple, required: true }, + { id: 'b1', name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'c1', name: 'c', style: HttpParamStyles.Simple, required: true }, ], query: [ - { name: 'b', style: HttpParamStyles.Form, required: true }, - { name: 'c', style: HttpParamStyles.Form, required: true }, + { id: 'd1', name: 'b', style: HttpParamStyles.Form, required: true }, + { id: 'e1', name: 'c', style: HttpParamStyles.Form, required: true }, ], path: [ - { name: 'a', style: HttpParamStyles.Simple, required: true }, - { name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'f1', name: 'a', style: HttpParamStyles.Simple, required: true }, + { id: 'g1', name: 'b', style: HttpParamStyles.Simple, required: true }, ], body: { + id: 'h1', description: 'And the yanks they were within', required: true, contents: [ { + id: 'h2', mediaType: 'application/json', schema: { type: 'number' }, }, @@ -406,27 +492,29 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200', headers: [], contents: [] }], + responses: [{ id: 'a', code: '200', headers: [], contents: [] }], request: { headers: [ - { name: 'a', style: HttpParamStyles.Simple, required: false }, - { name: 'b', style: HttpParamStyles.Simple, required: true }, - { name: 'c', style: HttpParamStyles.Simple, required: false }, + { id: 'b', name: 'a', style: HttpParamStyles.Simple, required: false }, + { id: 'c', name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'c1', name: 'c', style: HttpParamStyles.Simple, required: false }, ], query: [ - { name: 'a', style: HttpParamStyles.Form, required: false }, - { name: 'b', style: HttpParamStyles.Form, required: true }, - { name: 'c', style: HttpParamStyles.Form, required: false }, + { id: 'd', name: 'a', style: HttpParamStyles.Form, required: false }, + { id: 'e', name: 'b', style: HttpParamStyles.Form, required: true }, + { id: 'e1', name: 'c', style: HttpParamStyles.Form, required: false }, ], path: [ - { name: 'a', style: HttpParamStyles.Simple, required: true }, - { name: 'b', style: HttpParamStyles.Simple, required: false }, + { id: 'f', name: 'a', style: HttpParamStyles.Simple, required: true }, + { id: 'g1', name: 'b', style: HttpParamStyles.Simple, required: false }, ], body: { + id: 'g', required: true, description: 'The cadillac stood by the house; And the yanks they were within', contents: [ { + id: 'g1', mediaType: 'application/json', schema: { anyOf: [{ type: 'string' }, { type: 'number' }] }, }, diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts deleted file mode 100644 index 57cb1795..00000000 --- a/src/__tests__/utils.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { maybeResolveLocalRef } from '../utils'; - -describe('maybeResolveLocalRef()', () => { - it('follows $refs', () => { - expect( - maybeResolveLocalRef( - { - a: { - $ref: '#/b', - }, - b: { - c: 'woo', - }, - }, - { $ref: '#/a/c' }, - ), - ).toEqual('woo'); - }); - - it('handles invalid $refs', () => { - expect(maybeResolveLocalRef({ a: true }, { $ref: '#a' })).toStrictEqual({ $ref: '#a' }); - }); - - it('handles circular references', () => { - const document = { - get a(): unknown { - return this.a; - }, - }; - - expect( - maybeResolveLocalRef(document, { - $ref: '#/a', - }), - ).toStrictEqual({ $ref: '#/a' }); - }); -}); diff --git a/src/context.ts b/src/context.ts index d13668e0..0a4c3334 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,9 +1,67 @@ -import { TransformerContext } from './types'; -import { maybeResolveLocalRef } from './utils'; +import { hasRef, isLocalRef } from '@stoplight/json'; +// @ts-ignore +import * as fnv from 'fnv-plus'; -export function createContext>(document: T): TransformerContext { +import type { + AvailableContext, + Fragment, + IdGenerator, + RefResolver, + TransformerContext, + TranslateFunction, +} from './types'; + +export const DEFAULT_ID_GENERATOR: IdGenerator = input => fnv.fast1a52hex(input); + +export function createContext>( + document: T, + resolveRef: RefResolver, + generateId: IdGenerator, +): TransformerContext { + let context: AvailableContext = 'service'; return { document, - maybeResolveLocalRef: maybeResolveLocalRef.bind(null, document), + get context() { + return context; + }, + set context(value) { + context = value; + if (value !== 'operation') { + this.parentId = this.ids[value]; + } + }, + maybeResolveLocalRef(target: unknown) { + if (hasRef(target) && isLocalRef(target.$ref)) { + try { + return resolveRef.call(this, target); + } catch { + return target; + } + } + + return target; + }, + generateId(value) { + this.parentId = generateId(value); + return this.parentId; + }, + ids: { + service: '', + path: '', + operation: '', + }, + parentId: '', + }; +} + +export function withContext = TranslateFunction>( + fn: F, +): F { + return function (...args: Parameters) { + const { context, parentId } = this; + const result = fn.apply(this, args); + this.context = context; + this.parentId = parentId; + return result; }; } diff --git a/src/merge.ts b/src/merge.ts index b007adae..89f30131 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -8,7 +8,8 @@ import { IServer, } from '@stoplight/types'; import type { JSONSchema7 as JSONSchema } from 'json-schema'; -import isEqual = require('lodash.isequal'); + +import { isEqual } from './utils'; function isExclusivelyAnyOfSchema(schema: JSONSchema): schema is { anyOf: JSONSchema[] } { return !!(schema.anyOf && Object.keys(schema).length === 1); @@ -66,6 +67,7 @@ const mergeContents = mergeLists( (c1, c2) => c1.mediaType.toLowerCase() === c2.mediaType.toLowerCase(), (c1, c2) => { return { + id: c1.id, mediaType: c1.mediaType, schema: mergeTwo(mergeSchemas, c1.schema, c2.schema), examples: mergeContentExamples([c1.examples, c2.examples]), @@ -107,6 +109,7 @@ export const mergeResponses = mergeLists( function mergeRequestBodies(b1: IHttpOperationRequestBody, b2: IHttpOperationRequestBody): IHttpOperationRequestBody { return { + id: b1.id, description: [b1.description, b2.description].filter(Boolean).join('; ') || undefined, required: b1.required && b2.required, contents: mergeContents(b1.contents || [], b2.contents || []), diff --git a/src/oas/__tests__/__snapshots__/operation.test.ts.snap b/src/oas/__tests__/__snapshots__/operation.test.ts.snap index be3cd93d..12c6edbd 100644 --- a/src/oas/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/operation.test.ts.snap @@ -11,7 +11,7 @@ Array [ }, "x-test": true, }, - "id": "?http-operation-id?", + "id": "9ab1c1339675f", "iid": "addPet", "internal": false, "method": "post", @@ -21,22 +21,113 @@ Array [ "contents": Array [ Object { "examples": Array [], + "id": "becbc0ed96d9d", "mediaType": "application/json", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, Object { "examples": Array [], + "id": "7e79049c5af23", "mediaType": "application/xml", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, ], "description": "Pet object that needs to be added to the store", + "id": "62e6080a56c45", "required": true, }, "cookie": Array [], @@ -50,12 +141,12 @@ Array [ "contents": Array [], "description": "Invalid input", "headers": Array [], + "id": "029110671da4a", }, ], "security": Array [ Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -65,6 +156,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -72,10 +164,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -83,6 +177,7 @@ Array [ "summary": "Add a new pet to the store", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], @@ -90,7 +185,7 @@ Array [ Object { "deprecated": false, "extensions": Object {}, - "id": "?http-operation-id?", + "id": "29e0f9745e980", "internal": false, "method": "get", "path": "/pets", @@ -99,11 +194,13 @@ Array [ "headers": Array [ Object { "deprecated": false, + "id": "b93f4ef60cf1d", "name": "Rate-Limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9360fa7dc92ab", }, "style": "simple", }, @@ -112,21 +209,25 @@ Array [ "query": Array [ Object { "deprecated": false, + "id": "e1c08deab8b36", "name": "skip", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "828c85d6a2307", }, "style": "form", }, Object { "deprecated": false, + "id": "d04bcf1c141cf", "name": "limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "cdfdf74babd0b", }, "style": "form", }, @@ -138,15 +239,18 @@ Array [ "contents": Array [], "description": "", "headers": Array [], + "id": "6ae68ec4738b2", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -157,7 +261,7 @@ Array [ Object { "deprecated": false, "extensions": Object {}, - "id": "?http-operation-id?", + "id": "8bd67834e0236", "internal": false, "method": "options", "path": "/pets", @@ -166,11 +270,13 @@ Array [ "headers": Array [ Object { "deprecated": false, + "id": "b93f4ef60cf1d", "name": "Rate-Limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9360fa7dc92ab", }, "style": "simple", }, @@ -179,21 +285,25 @@ Array [ "query": Array [ Object { "deprecated": false, + "id": "e1c08deab8b36", "name": "skip", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "828c85d6a2307", }, "style": "form", }, Object { "deprecated": false, + "id": "d04bcf1c141cf", "name": "limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "cdfdf74babd0b", }, "style": "form", }, @@ -208,17 +318,20 @@ Array [ Object { "deprecated": false, "description": "Allowed clients", + "id": "0b9c41debc794", "name": "Allow", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "b5186fbb4fdf5", }, "style": "simple", }, Object { "deprecated": false, "description": "Remaining requests", + "id": "bbf28e682b54b", "name": "X-Rate-Limit", "required": false, "schema": Object { @@ -227,19 +340,23 @@ Array [ "maximum": 9223372036854776000, "minimum": -9223372036854776000, "type": "number", + "x-stoplight-id": "a07f1d06408fd", }, "style": "simple", }, ], + "id": "ee11fabc87ff7", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -251,7 +368,7 @@ Array [ "deprecated": false, "description": "", "extensions": Object {}, - "id": "?http-operation-id?", + "id": "4f51735d06d77", "internal": false, "method": "delete", "path": "/pets", @@ -267,21 +384,25 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], + "id": "5dfa6d9892b22", }, Object { "code": "404", "contents": Array [], "description": "Pet not found", "headers": Array [], + "id": "5dba6d9892c7e", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -293,7 +414,7 @@ Array [ "deprecated": false, "description": "", "extensions": Object {}, - "id": "?http-operation-id?", + "id": "fc497ea5adb47", "iid": "updatePet", "internal": false, "method": "put", @@ -303,22 +424,113 @@ Array [ "contents": Array [ Object { "examples": Array [], + "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, Object { "examples": Array [], + "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, ], "description": "Pet object that needs to be added to the store", + "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -326,11 +538,13 @@ Array [ "path": Array [ Object { "deprecated": false, + "id": "1ee79143656f4", "name": "petId", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "3c1327003ffa9", }, "style": "simple", }, @@ -343,41 +557,69 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], + "id": "c12ebf63598e3", }, Object { "code": "404", "contents": Array [ Object { "examples": Array [], + "id": "3d04477dddecb", "mediaType": "application/xml", "schema": Object { - "$ref": "#/definitions/Error", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "code": Object { + "type": "string", + }, + "message": Object { + "type": "string", + }, + }, + "required": Array [ + "code", + ], + "type": "object", + "x-stoplight-id": "b3c54fc227df9", }, }, Object { "examples": Array [], + "id": "d1be05066ac36", "mediaType": "application/json", "schema": Object { - "$ref": "#/definitions/Error", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "code": Object { + "type": "string", + }, + "message": Object { + "type": "string", + }, + }, + "required": Array [ + "code", + ], + "type": "object", + "x-stoplight-id": "b3c54fc227df9", }, }, ], "description": "Our shared 404 response.", "headers": Array [], + "id": "e1deb92ee1e73", }, Object { "code": "405", "contents": Array [], "description": "Validation exception", "headers": Array [], + "id": "c15ebf6359d8c", }, ], "security": Array [ Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -387,6 +629,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -394,10 +637,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -405,6 +650,7 @@ Array [ "summary": "Update an existing pet", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], @@ -415,6 +661,7 @@ Array [ exports[`oas operation openapi v3 1`] = ` Array [ Object { + "deprecated": false, "description": "", "extensions": Object { "x-another": Object { @@ -422,8 +669,9 @@ Array [ }, "x-test": true, }, - "id": "?http-operation-id?", + "id": "9ab1c1339675f", "iid": "addPet", + "internal": false, "method": "post", "path": "/pets", "request": Object { @@ -432,6 +680,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -476,6 +725,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -484,6 +734,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -528,6 +779,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -535,6 +787,7 @@ Array [ }, ], "description": "Pet object that needs to be added to the store", + "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -548,6 +801,7 @@ Array [ "contents": Array [], "description": "Invalid input", "headers": Array [], + "id": "029110671da4a", }, ], "security": Array [ @@ -562,6 +816,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -569,10 +824,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -580,18 +837,22 @@ Array [ "summary": "Add a new pet to the store", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], }, Object { + "deprecated": false, "extensions": Object {}, - "id": "?http-operation-id?", + "id": "29e0f9745e980", + "internal": false, "method": "get", "path": "/pets", "request": Object { "body": Object { "contents": Array [], + "id": "ccf0943cc3b92", }, "cookie": Array [], "headers": Array [], @@ -601,10 +862,13 @@ Array [ "deprecated": false, "examples": Array [], "explode": false, + "id": "e1c08deab8b36", "name": "skip", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "828c85d6a2307", }, "style": "simple", }, @@ -612,10 +876,13 @@ Array [ "deprecated": false, "examples": Array [], "explode": false, + "id": "d04bcf1c141cf", "name": "limit", + "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "cdfdf74babd0b", }, "style": "simple", }, @@ -628,25 +895,30 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "61b0e53aa1cce", "mediaType": "*/*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "properties": Object {}, "type": "object", + "x-stoplight-id": "8ea4aec4c2854", }, }, ], "description": "OK", "headers": Array [], + "id": "6ae68ec4738b2", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -655,10 +927,12 @@ Array [ "tags": Array [], }, Object { + "deprecated": false, "description": "", "extensions": Object {}, - "id": "?http-operation-id?", + "id": "fc497ea5adb47", "iid": "updatePet", + "internal": false, "method": "put", "path": "/pet/{petId}", "request": Object { @@ -667,6 +941,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -711,6 +986,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -719,6 +995,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -763,6 +1040,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -770,6 +1048,7 @@ Array [ }, ], "description": "Pet object that needs to be added to the store", + "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -779,10 +1058,13 @@ Array [ "deprecated": false, "examples": Array [], "explode": false, + "id": "1ee79143656f4", "name": "petId", + "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "3c1327003ffa9", }, "style": "simple", }, @@ -795,6 +1077,7 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], + "id": "c12ebf63598e3", }, Object { "code": "404", @@ -802,6 +1085,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "edbd8b027c519", "mediaType": "*/*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -817,17 +1101,20 @@ Array [ "code", ], "type": "object", + "x-stoplight-id": "b9501a9ded2c5", }, }, ], "description": "Our shared 404 response.", "headers": Array [], + "id": "e1deb92ee1e73", }, Object { "code": "405", "contents": Array [], "description": "Validation exception", "headers": Array [], + "id": "c15ebf6359d8c", }, ], "security": Array [ @@ -842,6 +1129,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -849,10 +1137,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -860,6 +1150,7 @@ Array [ "summary": "Update an existing pet", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], diff --git a/src/oas/__tests__/__snapshots__/service.test.ts.snap b/src/oas/__tests__/__snapshots__/service.test.ts.snap index 62583794..20fc18d1 100644 --- a/src/oas/__tests__/__snapshots__/service.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/service.test.ts.snap @@ -6,7 +6,7 @@ Object { "email": "apiteam@swagger.io", }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key \`special-key\` to test the authorization filters.", - "id": "?http-service-id?", + "id": "abc", "license": Object { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", @@ -14,7 +14,6 @@ Object { "name": "Swagger Petstore", "securitySchemes": Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -24,11 +23,12 @@ Object { }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, Object { - "description": undefined, + "id": "39070f74aaf58", "in": "header", "key": "api_key", "name": "api_key", @@ -37,10 +37,12 @@ Object { ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -48,14 +50,17 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", + "id": "bfc2f4aa9bced", "name": "pet", }, Object { "description": "Access to Petstore orders", + "id": "20c6cf86f6d6d", "name": "store", }, Object { "description": "Operations about user", + "id": "10e73f5f26c07", "name": "user", }, ], @@ -70,7 +75,7 @@ Object { "email": "apiteam@swagger.io", }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key \`special-key\` to test the authorization filters.", - "id": "?http-service-id?", + "id": "abc", "license": Object { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", @@ -87,10 +92,12 @@ Object { }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, Object { + "id": "39070f74aaf58", "in": "header", "key": "api_key", "name": "api_key", @@ -99,10 +106,12 @@ Object { ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -110,14 +119,17 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", + "id": "bfc2f4aa9bced", "name": "pet", }, Object { "description": "Access to Petstore orders", + "id": "20c6cf86f6d6d", "name": "store", }, Object { "description": "Operations about user", + "id": "10e73f5f26c07", "name": "user", }, ], diff --git a/src/oas/__tests__/accessors.test.ts b/src/oas/__tests__/accessors.test.ts index 29879b23..f608f4b0 100644 --- a/src/oas/__tests__/accessors.test.ts +++ b/src/oas/__tests__/accessors.test.ts @@ -1,80 +1,116 @@ -import { getValidOasParameters } from '../accessors'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../context'; +import { Fragment } from '../../types'; +import { createOasParamsIterator } from '../accessors'; +import { resolveRef } from '../resolver'; import { OasVersion } from '../types'; -describe('getOasParameters', () => { - it('should return empty array', () => { - expect(getValidOasParameters({}, OasVersion.OAS2, undefined, undefined)).toEqual([]); - }); +const getValidOasParameters = (document: { paths: { '/': { get: Fragment } } }, spec: OasVersion) => + Array.from( + createOasParamsIterator(spec as any).call( + createContext(document, resolveRef, DEFAULT_ID_GENERATOR), + document.paths['/'], + document.paths['/'].get, + ), + ); - it('should fallback to operation parameters', () => { - expect( - getValidOasParameters( - {}, - OasVersion.OAS2, - [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, - ], - undefined, - ), - ).toEqual([ - { - in: 'header', - name: 'n1', - }, - { - in: 'query', - name: 'n2', - }, - ]); - }); +describe('createOasParamsIterator', () => { + describe.each([OasVersion.OAS2, OasVersion.OAS3])('given %s spec', spec => { + it('should return empty array', () => { + const document = { + paths: { + '/': { + get: {}, + }, + }, + }; - it('should fallback to path parameters', () => { - expect( - getValidOasParameters({}, OasVersion.OAS2, undefined, [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, - ]), - ).toEqual([ - { - in: 'header', - name: 'n1', - }, - { - in: 'query', - name: 'n2', - }, - ]); - }); + expect(getValidOasParameters(document, spec)).toEqual([]); + }); + + it('should fallback to operation parameters', () => { + const document = { + paths: { + '/': { + get: { + parameters: [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, + ], + }, + }, + }, + }; + + expect(getValidOasParameters(document, spec)).toEqual([ + { + in: 'header', + name: 'n1', + }, + { + in: 'query', + name: 'n2', + }, + ]); + }); + + it('should fallback to path parameters', () => { + const document = { + paths: { + '/': { + parameters: [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, + ], + get: {}, + }, + }, + }; + + expect(getValidOasParameters(document, spec)).toEqual([ + { + in: 'header', + name: 'n1', + }, + { + in: 'query', + name: 'n2', + }, + ]); + }); + + it('should prefer operation parameters', () => { + const document = { + paths: { + '/': { + parameters: [ + { name: 'n1', in: 'query', type: 'string' }, + { name: 'np3', in: 'header' }, + ], + get: { + parameters: [ + { name: 'n1', in: 'query', type: 'array' }, + { name: 'no2', in: 'header' }, + ], + }, + }, + }, + }; - it('should prefer operation parameters', () => { - expect( - getValidOasParameters( - {}, - OasVersion.OAS3, - [ - { name: 'n1', in: 'query', type: 'array' }, - { name: 'no2', in: 'header' }, - ], - [ - { name: 'n1', in: 'query', type: 'string' }, - { name: 'np3', in: 'header' }, - ], - ), - ).toEqual([ - { - in: 'query', - name: 'n1', - type: 'array', - }, - { - in: 'header', - name: 'no2', - }, - { - in: 'header', - name: 'np3', - }, - ]); + expect(getValidOasParameters(document, spec)).toEqual([ + { + in: 'query', + name: 'n1', + type: 'array', + }, + { + in: 'header', + name: 'no2', + }, + { + in: 'header', + name: 'np3', + }, + ]); + }); }); }); diff --git a/src/oas/__tests__/fixtures/oas2-kitchen-sink.json b/src/oas/__tests__/fixtures/oas2-kitchen-sink.json index 3eef66d3..77415bab 100644 --- a/src/oas/__tests__/fixtures/oas2-kitchen-sink.json +++ b/src/oas/__tests__/fixtures/oas2-kitchen-sink.json @@ -1,4 +1,5 @@ { + "x-stoplight-id": "abc", "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", diff --git a/src/oas/__tests__/fixtures/oas3-kitchen-sink.json b/src/oas/__tests__/fixtures/oas3-kitchen-sink.json index de2dca89..14c550d3 100644 --- a/src/oas/__tests__/fixtures/oas3-kitchen-sink.json +++ b/src/oas/__tests__/fixtures/oas3-kitchen-sink.json @@ -1,4 +1,5 @@ { + "x-stoplight-id": "abc", "openapi": "3.0.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 2a0b0884..44471478 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -15,6 +15,6 @@ describe('oas service', () => { it('openapi v2 and v3 should transform in the same way', () => { const oas2MappedService = transformOas2Service({ document: oas2KitchenSinkJson as any }); const oas3MappedService = transformOas3Service({ document: oas3KitchenSinkJson as any }); - expect(oas2MappedService).toEqual(oas3MappedService); + expect(oas2MappedService).toStrictEqual(oas3MappedService); }); }); diff --git a/src/oas/__tests__/tags.test.ts b/src/oas/__tests__/tags.test.ts index 9efcbaa5..852c3a0a 100644 --- a/src/oas/__tests__/tags.test.ts +++ b/src/oas/__tests__/tags.test.ts @@ -1,7 +1,9 @@ -import { createContext } from '../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../context'; +import { resolveRef } from '../resolver'; import { translateToTags as _translateToTags } from '../tags'; -const translateToTags = (tags: unknown) => _translateToTags.call(createContext({}), tags); +const translateToTags = (tags: unknown) => + _translateToTags.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), tags); describe('translateToTags', () => { describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { @@ -13,6 +15,7 @@ describe('translateToTags', () => { it('should filter out invalid values', () => { expect(translateToTags([{}, null, 'foo'])).toStrictEqual([ { + id: expect.any(String), name: 'foo', }, ]); @@ -21,12 +24,15 @@ describe('translateToTags', () => { it('should normalize values', () => { expect(translateToTags([0, 'foo', true])).toStrictEqual([ { + id: expect.any(String), name: '0', }, { + id: expect.any(String), name: 'foo', }, { + id: expect.any(String), name: 'true', }, ]); @@ -35,12 +41,15 @@ describe('translateToTags', () => { it('should translate array of strings to tags', () => { expect(translateToTags(['a', 'b', 'c'])).toStrictEqual([ { + id: expect.any(String), name: 'a', }, { + id: expect.any(String), name: 'b', }, { + id: expect.any(String), name: 'c', }, ]); diff --git a/src/oas/accessors.ts b/src/oas/accessors.ts index 50675aad..63acf593 100644 --- a/src/oas/accessors.ts +++ b/src/oas/accessors.ts @@ -1,50 +1,44 @@ import type { Extensions } from '@stoplight/types'; -import { Fragment } from '../types'; -import { entries, maybeResolveLocalRef } from '../utils'; +import { Fragment, TransformerContext } from '../types'; +import { entries } from '../utils'; import { isValidOas2Param, isValidOas3Param, Oas2ParamBase, Oas3ParamBase, ParamBase } from './guards'; import { OasVersion } from './types'; const ROOT_EXTENSIONS = ['x-internal']; -function getParameters(document: Fragment, spec: OasVersion.OAS2, params: unknown): Oas2ParamBase[]; -function getParameters(document: Fragment, spec: OasVersion.OAS3, params: unknown): Oas3ParamBase[]; -function getParameters(document: Fragment, spec: OasVersion, params: unknown): ParamBase[] { - if (!Array.isArray(params)) return []; +const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; - const resolved = params.map(maybeResolveLocalRef.bind(null, document)); - return spec === OasVersion.OAS2 ? resolved.filter(isValidOas2Param) : resolved.filter(isValidOas3Param); -} +type OasParamsIterator = (this: TransformerContext, path: Fragment, operation: Fragment) => Iterable; -const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; +export function createOasParamsIterator(spec: OasVersion.OAS2): OasParamsIterator; +export function createOasParamsIterator(spec: OasVersion.OAS3): OasParamsIterator; +export function createOasParamsIterator(spec: OasVersion): OasParamsIterator { + return function* (path, operation) { + const seenParams = new Set(); + const { parentId, context } = this; + const opParams = Array.isArray(operation.parameters) ? operation.parameters : []; + const params = [...opParams, ...(Array.isArray(path.parameters) ? path.parameters : [])]; + + for (let i = 0; i < params.length; i++) { + const param = this.maybeResolveLocalRef(params[i]); + if (!(spec === OasVersion.OAS2 ? isValidOas2Param : isValidOas3Param)(param)) continue; + + const key = getIdForParameter(param); + + if (seenParams.has(key)) continue; + seenParams.add(key); + + if (this.context !== 'service') { + this.context = i < opParams.length ? 'operation' : 'path'; + } + + yield param; + } -export function getValidOasParameters( - document: Fragment, - spec: OasVersion.OAS2, - operationParams: unknown, - pathParams: unknown, -): Oas2ParamBase[]; -export function getValidOasParameters( - document: Fragment, - spec: OasVersion.OAS3, - operationParams: unknown, - pathParams: unknown, -): Oas3ParamBase[]; -export function getValidOasParameters( - document: Fragment, - spec: any, - operationParams: unknown, - pathParams: unknown, -): ParamBase[] { - const uniqueParameters: Record = {}; - - const params = [...getParameters(document, spec, operationParams), ...getParameters(document, spec, pathParams)]; - - for (const param of params) { - uniqueParameters[getIdForParameter(param)] ??= param; - } - - return Object.values(uniqueParameters); + this.context = context; + this.parentId = parentId; + }; } export function getExtensions(target: unknown): Extensions { diff --git a/src/oas/operation.ts b/src/oas/operation.ts index 94bb6864..738f67ee 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,9 +1,14 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, IHttpOperation } from '@stoplight/types'; -import type { OpenAPIObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; +import { isString } from '../guards'; import type { HttpOperationTransformer } from '../types'; +import { TranslateFunction } from '../types'; +import { getExtensions } from './accessors'; +import { translateToTags } from './tags'; const DEFAULT_METHODS = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch', 'trace']; @@ -32,3 +37,51 @@ export function transformOasOperations( ); }); } + +function wipePathParams(p: string) { + return p.replace(/({)[^}]+(?=})/g, '$1'); +} + +export const transformOasOperation: TranslateFunction< + DeepPartial | DeepPartial, + [path: string, method: string], + Omit +> = function (path: string, method: string) { + const pathObj = this.maybeResolveLocalRef(this.document?.paths?.[path]) as PathsObject; + if (typeof pathObj !== 'object' || pathObj === null) { + throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); + } + + const operation = this.maybeResolveLocalRef(pathObj[method]) as OperationObject; + if (!operation) { + throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); + } + + const reducedPath = wipePathParams(path); + const serviceId = (this.ids.service = String(this.document['x-stoplight-id'])); + this.ids.path = this.generateId(`http_path-${this.ids.service}-${reducedPath}`); + const operationId = (this.ids.operation = this.generateId(`http_operation-${serviceId}-${method}-${reducedPath}`)); + + this.context = 'operation'; + + return { + id: operationId, + + deprecated: !!operation.deprecated, + internal: !!operation['x-internal'], + method, + path, + + tags: translateToTags.call(this, operation.tags), + extensions: getExtensions(operation), + + ...pickBy( + { + iid: operation.operationId, + description: operation.description, + summary: operation.summary, + }, + isString, + ), + }; +}; diff --git a/src/oas/resolveInlineRef.ts b/src/oas/resolveInlineRef.ts new file mode 100644 index 00000000..4e8a8b40 --- /dev/null +++ b/src/oas/resolveInlineRef.ts @@ -0,0 +1,57 @@ +import { extractSourceFromRef, isPlainObject, pointerToPath } from '@stoplight/json'; +import { Dictionary } from '@stoplight/types'; + +function _resolveInlineRef(document: Dictionary, ref: string, seen: unknown[], location: string[]): unknown { + const source = extractSourceFromRef(ref); + if (source !== null) { + throw new ReferenceError('Cannot resolve external references'); + } + + const path = pointerToPath(ref); + let value: unknown = document; + for (const segment of path) { + if ((!isPlainObject(value) && !Array.isArray(value)) || !(segment in value)) { + throw new ReferenceError(`Could not resolve '${ref}'`); + } + + value = value[segment]; + + if (isPlainObject(value) && '$ref' in value) { + if (seen.includes(value)) { + // circular, let's stop + return seen[seen.length - 1]; + } + + seen.push(value); + + if (typeof value.$ref !== 'string') { + throw new TypeError('$ref should be a string'); + } + + location.length = 0; + value = _resolveInlineRef(document, value.$ref, seen, location); + } else { + location.push(String(segment)); + } + } + + if (seen.length === 0) { + return value; + } + + const sourceDocument = seen[seen.length - 1]; + + if (isPlainObject(sourceDocument) && ('summary' in sourceDocument || 'description' in sourceDocument)) { + return { + ...(value as object), + ...('description' in sourceDocument ? { description: sourceDocument.description } : null), + ...('summary' in sourceDocument ? { summary: sourceDocument.summary } : null), + }; + } + + return value; +} + +export function resolveInlineRef(document: Dictionary, ref: string, location: string[] = []): unknown { + return _resolveInlineRef(document, ref, [], location); +} diff --git a/src/oas/resolver.ts b/src/oas/resolver.ts new file mode 100644 index 00000000..07da3ab7 --- /dev/null +++ b/src/oas/resolver.ts @@ -0,0 +1,32 @@ +import type { JsonPath } from '@stoplight/types'; + +import type { AvailableContext, RefResolver } from '../types'; +import { resolveInlineRef } from './resolveInlineRef'; + +function inferContext(path: JsonPath): AvailableContext { + if (path.length < 2 || path[0] !== 'paths') return 'service'; + if (path.length === 2 || path[3] === 'parameters' || path[3] === 'servers') return 'path'; + return 'operation'; +} + +const SHARED_COMPONENTS_KEYS = new WeakMap(); + +export function getSharedKey(value: object) { + return SHARED_COMPONENTS_KEYS.get(value); +} + +export const resolveRef: RefResolver = function (target) { + const location: string[] = []; + const resolved = resolveInlineRef(this.document, target.$ref, location); + + const context = inferContext(location); + if (context !== null && this.context !== context) { + this.context = context; + } + + if (typeof resolved === 'object' && resolved !== null && context === 'service') { + SHARED_COMPONENTS_KEYS.set(resolved, location[0] === 'components' ? location[2] : location[1]); + } + + return resolved; +}; diff --git a/src/oas/service.ts b/src/oas/service.ts index c6cb6743..c8fc8b81 100644 --- a/src/oas/service.ts +++ b/src/oas/service.ts @@ -13,9 +13,12 @@ import { translateLogo } from './transformers/translateLogo'; export const transformOasService: TranslateFunction | DeepPartial, [], IHttpService> = function () { const document = this.document; + const id = String(document['x-stoplight-id']); + this.ids.service = id; + this.parentId = id; const httpService: IHttpService = { - id: '?http-service-id?', + id, version: document.info?.version ?? '', name: document.info?.title ?? 'no-title', diff --git a/src/oas/tags.ts b/src/oas/tags.ts index 7f1ec25d..d74a78b4 100644 --- a/src/oas/tags.ts +++ b/src/oas/tags.ts @@ -1,21 +1,22 @@ import { isPlainObject } from '@stoplight/json'; import type { INodeTag, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); - +import { withContext } from '../context'; import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; import type { ArrayCallbackParameters, Fragment, TranslateFunction } from '../types'; -export const translateTag: TranslateFunction< - Fragment, - ArrayCallbackParameters, - Optional -> = function (tag) { - if (tag === null || !isSerializablePrimitive(tag)) return; +const translateTag = withContext, Optional>>( + function (tag) { + if (tag === null || !isSerializablePrimitive(tag)) return; - return { - name: String(tag), - }; -}; + const name = String(tag); + + return { + id: this.generateId(`tag-${this.ids.service}-${name}`), + name, + }; + }, +); export const translateTagDefinition: TranslateFunction< Fragment, diff --git a/src/oas/transformers/examples.ts b/src/oas/transformers/examples.ts new file mode 100644 index 00000000..99b1dc70 --- /dev/null +++ b/src/oas/transformers/examples.ts @@ -0,0 +1,18 @@ +import { DeepPartial, INodeExample } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; + +import { withContext } from '../../context'; +import { TranslateFunction } from '../../types'; + +export const translateToDefaultExample = withContext< + TranslateFunction, [key: string, value: unknown], INodeExample> +>(function (key, value) { + const resolvedValue = this.maybeResolveLocalRef(value); + + return { + id: this.generateId(`example-${this.parentId}-${key}`), + value: resolvedValue, + key, + }; +}); diff --git a/src/oas/transformers/schema/__tests__/schema.spec.ts b/src/oas/transformers/schema/__tests__/schema.spec.ts index bea202ba..cca91501 100644 --- a/src/oas/transformers/schema/__tests__/schema.spec.ts +++ b/src/oas/transformers/schema/__tests__/schema.spec.ts @@ -1,8 +1,10 @@ -import { createContext } from '../../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../../context'; +import { resolveRef } from '../../../resolver'; import { translateSchemaObject } from '..'; import type { OASSchemaObject } from '../types'; -const translate = (schemaObject: OASSchemaObject) => translateSchemaObject.call(createContext({}), schemaObject); +const translate = (schemaObject: OASSchemaObject) => + translateSchemaObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), schemaObject); describe('translateSchemaObject', () => { it('should translate id', () => { @@ -16,6 +18,7 @@ describe('translateSchemaObject', () => { }, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -36,6 +39,7 @@ describe('translateSchemaObject', () => { exclusiveMaximum: true, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', exclusiveMinimum: 2, @@ -68,6 +72,7 @@ describe('translateSchemaObject', () => { ], }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', anyOf: [ { @@ -110,6 +115,7 @@ describe('translateSchemaObject', () => { }, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { exclusiveMinimum: { @@ -152,6 +158,7 @@ describe('translateSchemaObject', () => { 'x-nullable': true, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: ['string', 'null'], }); @@ -164,6 +171,7 @@ describe('translateSchemaObject', () => { 'x-example': 'Cat', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', examples: ['Cat'], @@ -179,6 +187,7 @@ describe('translateSchemaObject', () => { nullable: true, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: ['string', 'null'], }); @@ -191,6 +200,7 @@ describe('translateSchemaObject', () => { example: 'Cat', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', examples: ['Cat'], @@ -204,6 +214,7 @@ describe('translateSchemaObject', () => { format: 'base64', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', contentEncoding: 'base64', @@ -217,6 +228,7 @@ describe('translateSchemaObject', () => { format: 'binary', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', contentMediaType: 'application/octet-stream', diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index cd12e29c..029d1159 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -4,7 +4,9 @@ import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; import type { OpenAPIObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; -import type { TranslateFunction } from '../../../types'; +import { withContext } from '../../../context'; +import { TranslateFunction } from '../../../types'; +import { getSharedKey } from '../../resolver'; import keywords from './keywords'; import type { OASSchemaObject } from './types'; @@ -14,28 +16,46 @@ type InternalOptions = { structs: string[]; }; +const CACHE = new WeakMap(); + // Convert from OpenAPI 2.0, OpenAPI 3.0 `SchemaObject` or JSON Schema Draft4/6 to JSON Schema Draft 7 // This converter shouldn't make any differences to Schema objects defined in OpenAPI 3.1, excepts when jsonSchemaDialect is provided. -export const translateSchemaObject: TranslateFunction< - DeepPartial, - [schema: OASSchemaObject], - JSONSchema7 -> = function (schema) { +export const translateSchemaObject = withContext< + TranslateFunction< + DeepPartial, + [schema: OASSchemaObject], + JSONSchema7 + > +>(function (schema) { const document = this.document; + const resolvedSchema = this.maybeResolveLocalRef(schema); + if (!isPlainObject(resolvedSchema)) return {}; + let cached = CACHE.get(resolvedSchema); + if (cached) { + return { ...cached }; + } + + const actualKey = this.context === 'service' ? getSharedKey(resolvedSchema) : ''; + const id = this.generateId(`schema-${this.parentId}-${actualKey}`); if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { - return { + cached = { $schema: document.jsonSchemaDialect, // let's assume it's draft 7, albeit it might be draft 2020-12 or 2019-09. // it's a safe bet, because there was only _one_ relatively minor breaking change introduced between Draft 7 and 2020-12. - ...(schema as JSONSchema7), + ...(resolvedSchema as JSONSchema7), + 'x-stoplight-id': id, }; + } else { + cached = convertSchema(resolvedSchema); + cached['x-stoplight-id'] = id; } - return convertSchema(schema); -}; + CACHE.set(resolvedSchema, cached); + return cached; +}); -export function convertSchema(schema: OASSchemaObject) { +export function convertSchema(schema: OASSchemaObject): JSONSchema7 { const clonedSchema = _convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); diff --git a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap index d58bd9bb..be67c303 100644 --- a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap @@ -5,7 +5,7 @@ Object { "deprecated": true, "description": "odesc", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "oid", "internal": false, "method": "get", @@ -23,10 +23,12 @@ Object { Object { "examples": Array [ Object { + "id": Any, "key": "example", "value": Object {}, }, ], + "id": Any, "mediaType": "", "schema": Object {}, }, @@ -35,21 +37,23 @@ Object { "headers": Array [ Object { "deprecated": false, + "id": Any, "name": "header", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "integer", + "x-stoplight-id": Any, }, "style": "simple", }, ], + "id": Any, }, ], "security": Array [ Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -58,13 +62,14 @@ Object { }, }, }, + "id": Any, "key": "petstore_auth", "type": "oauth2", }, ], Array [ Object { - "description": undefined, + "id": Any, "in": "header", "key": "api_key", "name": "api_key_name", diff --git a/src/oas2/__tests__/guards.test.ts b/src/oas2/__tests__/guards.test.ts deleted file mode 100644 index 67051a85..00000000 --- a/src/oas2/__tests__/guards.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { isSecurityScheme, isTagObject } from '../guards'; - -describe('guards', () => { - describe('isSecurityScheme()', () => { - it('should return true for scheme with type', () => { - expect( - isSecurityScheme({ - type: 'apiKey', - }), - ).toEqual(true); - }); - - it('should return false for non object', () => { - expect(isSecurityScheme(undefined)).toEqual(false); - }); - - it('should return false for missing type property', () => { - expect(isSecurityScheme({ foo: 'bar' })).toEqual(false); - }); - }); - - describe('isTagObject()', () => { - it('should return true for scheme with name', () => { - expect( - isTagObject({ - name: 'foo', - }), - ).toEqual(true); - }); - - it('should return false for non object', () => { - expect(isTagObject(undefined)).toEqual(false); - }); - - it('should return false for missing name property', () => { - expect(isTagObject({ foo: 'bar' })).toEqual(false); - }); - }); -}); diff --git a/src/oas2/__tests__/operation.test.ts b/src/oas2/__tests__/operation.test.ts index 7e010fef..a91f0583 100644 --- a/src/oas2/__tests__/operation.test.ts +++ b/src/oas2/__tests__/operation.test.ts @@ -68,7 +68,44 @@ describe('transformOas2Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + responses: [ + { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + }, + ], + headers: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + ], + security: [ + [ + { + id: expect.any(String), + }, + ], + [ + { + id: expect.any(String), + }, + ], + ], + }); }); it('should properly translate operation with no response body', () => { @@ -155,7 +192,7 @@ describe('transformOas2Operation', () => { extensions: {}, }), { - id: '?http-operation-id?', + id: expect.any(String), deprecated: false, internal: false, path: '/users/{userId}', @@ -200,7 +237,7 @@ describe('transformOas2Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), deprecated: false, internal: false, method: 'get', @@ -209,10 +246,12 @@ describe('transformOas2Operation', () => { cookie: [], headers: [ { + id: expect.any(String), name: 'name', required: false, deprecated: false, schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', }, style: 'simple', diff --git a/src/oas2/__tests__/service.test.ts b/src/oas2/__tests__/service.test.ts index 754cd2f5..161e5067 100644 --- a/src/oas2/__tests__/service.test.ts +++ b/src/oas2/__tests__/service.test.ts @@ -1,11 +1,13 @@ import { OpenAPIObject } from 'openapi3-ts'; import { Spec } from 'swagger-schema-official'; +import { Fragment } from '../../types'; import { transformOas2Service } from '../service'; describe('oas2 service', () => { it('should handle non array schemes', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'def', schemes: 2 as any, }; @@ -14,14 +16,15 @@ describe('oas2 service', () => { document, }), ).toStrictEqual({ - id: '?http-service-id?', + id: 'def', name: 'no-title', version: '', }); }); it('should accept empty title', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'abc', host: 'petstore.swagger.io', basePath: '/v2', info: { @@ -36,15 +39,17 @@ describe('oas2 service', () => { document, }), ).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', servers: [ { + id: expect.any(String), name: '', url: 'https://petstore.swagger.io/v2', }, { + id: expect.any(String), name: '', url: 'http://petstore.swagger.io/v2', }, @@ -53,20 +58,22 @@ describe('oas2 service', () => { }); it('should handle invalid document securities gracefully', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'abc', securityDefinitions: {}, security: ['API-Key'] as any, }; expect(transformOas2Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', }); }); it('filters out scopes', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'def', swagger: '2.0', securityDefinitions: { 'API Key': { @@ -88,6 +95,7 @@ describe('oas2 service', () => { describe('x-logo support', () => { it('should support x-logo', () => { const document: Partial = { + 'x-stoplight-id': 'abc', info: { title: 'no-title', version: '1.0.0', @@ -101,7 +109,7 @@ describe('oas2 service', () => { }; expect(transformOas2Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '1.0.0', logo: { @@ -114,6 +122,7 @@ describe('oas2 service', () => { }); it('should provide default values for href and altText', () => { const document: Partial = { + 'x-stoplight-id': 'abc', info: { title: 'no-title', version: '1.0.0', @@ -125,7 +134,7 @@ describe('oas2 service', () => { }; expect(transformOas2Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', contact: { url: 'https://stoplight.io', diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index bbcfe522..1230a42b 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; -import { DeepPartial, Dictionary } from '@stoplight/types'; -import { Operation, Schema, Security, Spec } from 'swagger-schema-official'; +import type { DeepPartial } from '@stoplight/types'; +import type { Operation, Security, Spec } from 'swagger-schema-official'; import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../guards'; @@ -68,11 +68,11 @@ function getProducesOrConsumes( return mimeTypes.flat().filter(isString); } -export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Record { +export function getExamplesFromSchema(data: unknown): Record { if (!isPlainObject(data)) return {}; return { - ...('x-examples' in data && isPlainObject(data['x-examples']) && { ...data['x-examples'] }), + ...(isPlainObject(data['x-examples']) && { ...data['x-examples'] }), ...('example' in data && { default: data.example }), }; } diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index bf5d124f..cdfc6da3 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -33,3 +33,11 @@ export function isPathParam(param: unknown): param is Oas2ParamBase & { in: 'pat export function isHeaderParam(param: unknown): param is Oas2ParamBase & { in: 'header' } { return isValidOas2Param(param) && param.in === 'header'; } + +export function isBodyParam(param: unknown): param is Oas2ParamBase & { in: 'body' } { + return isValidOas2Param(param) && param.in === 'body'; +} + +export function isFormDataParam(param: unknown): param is Oas2ParamBase & { in: 'formData' } { + return isValidOas2Param(param) && param.in === 'formData'; +} diff --git a/src/oas2/index.ts b/src/oas2/index.ts index 0e972888..f2e98a7a 100644 --- a/src/oas2/index.ts +++ b/src/oas2/index.ts @@ -1,3 +1,2 @@ export * from './operation'; export * from './service'; -export * from './transformers/params'; diff --git a/src/oas2/operation.ts b/src/oas2/operation.ts index 30e022a3..0ee05d52 100644 --- a/src/oas2/operation.ts +++ b/src/oas2/operation.ts @@ -1,59 +1,32 @@ -import { isPlainObject } from '@stoplight/json'; -import type { IHttpOperation } from '@stoplight/types'; +import type { DeepPartial, IHttpOperation } from '@stoplight/types'; import type { Spec } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; -import { isString } from '../guards'; -import { getExtensions } from '../oas/accessors'; -import { transformOasOperations } from '../oas/operation'; -import { translateToTags } from '../oas/tags'; +import { createContext, DEFAULT_ID_GENERATOR } from '../context'; +import { transformOasOperation, transformOasOperations } from '../oas/operation'; +import { resolveRef } from '../oas/resolver'; import { Oas2HttpOperationTransformer } from '../oas/types'; -import { maybeResolveLocalRef } from '../utils'; +import type { Fragment } from '../types'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; import { translateToServers } from './transformers/servers'; -export function transformOas2Operations(document: Spec): IHttpOperation[] { +export function transformOas2Operations(document: DeepPartial): IHttpOperation[] { return transformOasOperations(document, transformOas2Operation); } -export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]); - if (!isPlainObject(pathObj)) { - throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); - } - - const operation = maybeResolveLocalRef(document, pathObj[method]); - if (!isPlainObject(operation)) { - throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); - } - - const ctx = createContext(document); +export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document: _document, path, method }) => { + const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); + const httpOperation = transformOasOperation.call(ctx, path, method); + const pathObj = ctx.maybeResolveLocalRef(ctx.document.paths![path]) as Fragment; + const operation = ctx.maybeResolveLocalRef(pathObj[method]) as Fragment; return { - id: '?http-operation-id?', - method, - path, - - deprecated: !!operation.deprecated, - internal: !!operation['x-internal'], + ...httpOperation, responses: translateToResponses.call(ctx, operation), servers: translateToServers.call(ctx, operation), request: translateToRequest.call(ctx, pathObj, operation), - tags: translateToTags.call(ctx, operation.tags), security: translateToSecurities.call(ctx, operation.security), - extensions: getExtensions(operation), - - ...pickBy( - { - iid: operation.operationId, - description: operation.description, - summary: operation.summary, - }, - isString, - ), }; }; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index a67f48d6..344282f5 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,8 +1,9 @@ import { isPlainObject } from '@stoplight/json'; import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../context'; import { isNonNullable, isString } from '../guards'; +import { resolveRef } from '../oas/resolver'; import { transformOasService } from '../oas/service'; import { Oas2HttpServiceTransformer } from '../oas/types'; import { entries } from '../utils'; @@ -10,7 +11,7 @@ import { translateToSingleSecurity } from './transformers/securities'; import { translateToServer } from './transformers/servers'; export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document); + const ctx = createContext(document, resolveRef, DEFAULT_ID_GENERATOR); const httpService = transformOasService.call(ctx); if (document.info?.license) { diff --git a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap index 5c25ea5f..a2a2dca2 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap @@ -6,26 +6,31 @@ Object { Object { "examples": Array [ Object { + "id": Any, "key": "example-1", "value": Object { "hello": "world", }, }, Object { + "id": Any, "key": "example-2", "value": Object { "foo": "bar", }, }, ], + "id": Any, "mediaType": "*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "e-mail", + "x-stoplight-id": "5d6bd328630ce", }, }, ], "description": "descr", + "id": Any, "required": true, } `; @@ -35,14 +40,17 @@ Object { "contents": Array [ Object { "examples": Array [], + "id": Any, "mediaType": "*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "e-mail", + "x-stoplight-id": Any, }, }, ], "description": "descr", + "id": Any, "required": true, } `; @@ -51,12 +59,14 @@ exports[`params.translator translateToHeaderParam should translate header param Object { "deprecated": false, "description": "desc", + "id": Any, "name": "name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "minimum": 12, "type": "integer", + "x-stoplight-id": "e6c28cf6b8000", }, "style": "simple", } @@ -69,22 +79,26 @@ Array [ Object { "deprecated": false, "description": "a description", + "id": "117996bbb6f45", "name": "header-name", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9259b14a1615b", }, "style": "simple", }, Object { "deprecated": false, "description": "another description", + "id": "dce8b374eb7d6", "name": "plain-tex", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "31f792c907f10", }, "style": "simple", }, @@ -96,11 +110,13 @@ Array [ Object { "deprecated": false, "description": "a description", + "id": "117996bbb6f45", "name": "header-name", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9259b14a1615b", }, "style": "simple", }, @@ -111,11 +127,13 @@ exports[`params.translator translateToPathParameter should translate 1`] = ` Object { "deprecated": false, "description": "descr", + "id": Any, "name": "name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": Any, }, "style": "simple", } diff --git a/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap index 88edbf9d..07b121c2 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -1,135 +1,135 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`responses should translate to multiple responses 1`] = ` -Array [ - Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [ - Object { - "key": "application/json", - "value": Object {}, - }, - ], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", +Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [ + Object { + "id": "3983e02fea0fe", + "key": "application/json", + "value": Object {}, }, + ], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - Object { - "examples": Array [], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, - }, - ], - "description": "d1", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", + }, + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - }, - Object { - "code": "r2", - "contents": Array [ - Object { - "examples": Array [], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, + }, + ], + "description": "d1", + "headers": Array [], + "id": Any, +} +`; + +exports[`responses should translate to multiple responses 2`] = ` +Object { + "code": "r2", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - Object { - "examples": Array [ - Object { - "key": "application/xml", - "value": Object {}, - }, - ], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", + }, + Object { + "examples": Array [ + Object { + "id": Any, + "key": "application/xml", + "value": Object {}, }, + ], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - "description": "d2", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", - }, - ], - }, -] + }, + ], + "description": "d2", + "headers": Array [], + "id": Any, +} `; exports[`responses should translate to response w/o examples 1`] = ` -Array [ - Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, - }, - Object { - "examples": Array [], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, +Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - "description": "d1", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", + }, + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - }, -] + }, + ], + "description": "d1", + "headers": Array [], + "id": Any, +} `; exports[`responses should translate to response w/o headers 1`] = ` -Array [ - Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, +Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - Object { - "examples": Array [ - Object { - "key": "application/xml", - "value": Object {}, - }, - ], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", + }, + Object { + "examples": Array [ + Object { + "id": Any, + "key": "application/xml", + "value": Object {}, }, + ], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - "description": "d1", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", - }, - ], - }, -] + }, + ], + "description": "d1", + "headers": Array [], + "id": Any, +} `; diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index 2b8158a8..d3cf309c 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -1,7 +1,8 @@ import { DeepPartial, HttpParamStyles } from '@stoplight/types'; import { FormDataParameter, QueryParameter, Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateFromFormDataParameters as _translateFromFormDataParameters, translateToBodyParameter as _translateToBodyParameter, @@ -14,28 +15,28 @@ import { const translateFromFormDataParameters = ( document: DeepPartial, ...params: Parameters -) => _translateFromFormDataParameters.call(createContext(document), ...params); +) => _translateFromFormDataParameters.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToBodyParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToBodyParameter.call(createContext(document), ...params); +) => _translateToBodyParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToHeaderParam = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParam.call(createContext(document), ...params); + _translateToHeaderParam.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToHeaderParams = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParams.call(createContext(document), ...params); + _translateToHeaderParams.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToPathParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToPathParameter.call(createContext(document), ...params); +) => _translateToPathParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToQueryParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToQueryParameter.call(createContext(document), ...params); +) => _translateToQueryParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); describe('params.translator', () => { let consumes = ['*']; @@ -54,7 +55,9 @@ describe('params.translator', () => { description: 'desc', }, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + }); }); }); @@ -112,7 +115,17 @@ describe('params.translator', () => { }, consumes, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('should preserve readOnly flag in schema', () => { @@ -133,7 +146,13 @@ describe('params.translator', () => { ).toEqual( expect.objectContaining({ contents: expect.arrayContaining([ - expect.objectContaining({ schema: { $schema: 'http://json-schema.org/draft-07/schema#', readOnly: true } }), + expect.objectContaining({ + schema: { + 'x-stoplight-id': expect.any(String), + $schema: 'http://json-schema.org/draft-07/schema#', + readOnly: true, + }, + }), ]), }), ); @@ -163,7 +182,22 @@ describe('params.translator', () => { }, consumes, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + { + id: expect.any(String), + }, + ], + }, + ], + }); }); describe('schema examples', () => { @@ -194,8 +228,8 @@ describe('params.translator', () => { contents: expect.arrayContaining([ expect.objectContaining({ examples: [ - { key: 'example-1', value: { hello: 'world' } }, - { key: 'example-2', value: { foo: 'bar' } }, + { id: expect.any(String), key: 'example-1', value: { hello: 'world' } }, + { id: expect.any(String), key: 'example-2', value: { foo: 'bar' } }, ], }), ]), @@ -232,7 +266,7 @@ describe('params.translator', () => { expect.objectContaining({ contents: expect.arrayContaining([ expect.objectContaining({ - examples: [{ key: 'example-2', value: { foo: 'bar' } }], + examples: [{ id: expect.any(String), key: 'example-2', value: { foo: 'bar' } }], }), ]), }), @@ -284,6 +318,7 @@ describe('params.translator', () => { it('converts parameters into schema', () => { const expectedContent = { + id: expect.any(String), encodings: [ { explode: true, @@ -292,9 +327,11 @@ describe('params.translator', () => { }, ], schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { arr: { + 'x-stoplight-id': expect.any(String), description: 'desc', items: { type: 'number', @@ -304,12 +341,14 @@ describe('params.translator', () => { type: 'array', }, int: { + 'x-stoplight-id': expect.any(String), description: 'desc', maximum: 3, minimum: 0, type: 'integer', }, str: { + 'x-stoplight-id': expect.any(String), minLength: 1, default: '25-07-2019', description: 'desc', @@ -329,6 +368,7 @@ describe('params.translator', () => { consumes, ), ).toEqual({ + id: expect.any(String), contents: [ Object.assign({}, expectedContent, { mediaType: 'application/x-www-form-urlencoded' }), Object.assign({}, expectedContent, { mediaType: 'multipart/form-data' }), @@ -378,7 +418,12 @@ describe('params.translator', () => { type: 'string', }, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); }); }); diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index 286e6788..a79c060d 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -6,7 +6,8 @@ import { QueryParameter, } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -19,7 +20,7 @@ import { translateToRequest as _translateToRequest } from '../request'; jest.mock('../params'); const translateToRequest = (path: Record, parameters: any[]) => { - const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }); + const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }, resolveRef, DEFAULT_ID_GENERATOR); return _translateToRequest.call(ctx, path, { parameters }); }; diff --git a/src/oas2/transformers/__tests__/responses.test.ts b/src/oas2/transformers/__tests__/responses.test.ts index 11881d59..eb29f18f 100644 --- a/src/oas2/transformers/__tests__/responses.test.ts +++ b/src/oas2/transformers/__tests__/responses.test.ts @@ -1,23 +1,16 @@ -import { DeepPartial, HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; +import { DeepPartial } from '@stoplight/types'; import { Operation, Schema, Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; -import { translateToHeaderParams } from '../params'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToResponses as _translateToResponses } from '../responses'; -jest.mock('../params'); - const translateToResponses = (document: DeepPartial, responses: DeepPartial) => - _translateToResponses.call(createContext(document), { responses }); + _translateToResponses.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), { responses }); describe('responses', () => { - const fakeHeaderParams: IHttpHeaderParam[] = [{ name: 'fake-header', style: HttpParamStyles.Simple }]; const produces = ['application/json', 'application/xml']; - beforeEach(() => { - (translateToHeaderParams as jest.Mock).mockReturnValue(fakeHeaderParams); - }); - it('should translate to multiple responses', () => { const responses = translateToResponses( { produces }, @@ -41,38 +34,117 @@ describe('responses', () => { }, ); - expect(responses).toMatchSnapshot(); + expect(responses).toHaveLength(2); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + examples: [], + }, + ], + }); + expect(responses[1]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + examples: [ + { + id: expect.any(String), + }, + ], + }, + ], + }); }); it('should translate to response w/o headers', () => { - expect( - translateToResponses( - { produces }, + const responses = translateToResponses( + { produces }, + { + r1: { + description: 'd1', + examples: { + 'application/xml': {}, + }, + schema: {}, + }, + }, + ); + + expect(responses).toHaveLength(1); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ { - r1: { - description: 'd1', - examples: { - 'application/xml': {}, - }, - schema: {}, + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), }, + examples: [ + { + id: expect.any(String), + }, + ], }, - ), - ).toMatchSnapshot(); + ], + }); }); it('should translate to response w/o examples', () => { - expect( - translateToResponses( - { produces }, + const responses = translateToResponses( + { produces }, + { + r1: { + description: 'd1', + schema: {}, + }, + }, + ); + + expect(responses).toHaveLength(1); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ { - r1: { - description: 'd1', - schema: {}, + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), }, }, - ), - ).toMatchSnapshot(); + ], + }); }); describe('should keep foreign examples', () => { @@ -117,7 +189,9 @@ describe('responses', () => { }, }, ); - expect(responses[0].contents![0]).toHaveProperty('examples', [{ key: 'default', value: { name: 'value' } }]); + expect(responses[0].contents![0]).toHaveProperty('examples', [ + { id: expect.any(String), key: 'default', value: { name: 'value' } }, + ]); }); }); @@ -143,8 +217,8 @@ describe('responses', () => { }, ); expect(responses[0].contents![0]).toHaveProperty('examples', [ - { key: 'application/json', value: { name: 'examples value' } }, - { key: 'default', value: { name: 'example value' } }, + { id: expect.any(String), key: 'application/json', value: { name: 'examples value' } }, + { id: expect.any(String), key: 'default', value: { name: 'example value' } }, ]); }); }); @@ -171,8 +245,8 @@ describe('responses', () => { ); expect(responses[0].contents![0].examples).toHaveLength(2); expect(responses[0].contents![0].examples).toEqual([ - { key: 'application/json', value: {} }, - { key: 'application/i-have-no-clue', value: {} }, + { id: expect.any(String), key: 'application/json', value: {} }, + { id: expect.any(String), key: 'application/i-have-no-clue', value: {} }, ]); }); }); diff --git a/src/oas2/transformers/__tests__/securities.test.ts b/src/oas2/transformers/__tests__/securities.test.ts index 9c60c069..c47e799d 100644 --- a/src/oas2/transformers/__tests__/securities.test.ts +++ b/src/oas2/transformers/__tests__/securities.test.ts @@ -1,11 +1,12 @@ import { DeepPartial } from '@stoplight/types'; import { Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToSecurities as _translateToSecurities } from '../securities'; const translateToSecurities = (document: DeepPartial, ...params: Parameters) => - _translateToSecurities.call(createContext(document), ...params); + _translateToSecurities.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); describe('securities', () => { describe('translateToSecurities', () => { @@ -40,6 +41,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', @@ -67,6 +69,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'header', @@ -94,6 +97,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), name: '', type: 'apiKey', in: 'header', @@ -140,6 +144,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-flow-security', @@ -167,6 +172,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: {} } }, key: 'implicit-flow-security', @@ -195,6 +201,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { password: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'password-flow-security', @@ -223,6 +230,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { clientCredentials: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'application-flow-security', @@ -252,6 +260,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { authorizationCode: { @@ -317,6 +326,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', @@ -325,6 +335,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', @@ -333,6 +344,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', @@ -349,18 +361,21 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', key: 'basic-security', }, { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', type: 'oauth2', }, { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 11cd45d6..0f10b8bf 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,13 +1,14 @@ import type { DeepPartial } from '@stoplight/types'; import type { Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToServers as _translateToServers } from '../servers'; type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; const translateToServers = (document: DeepPartial, ...params: Parameters) => - _translateToServers.call(createContext(document), ...params); + _translateToServers.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); describe('translateToServers', () => { afterAll(() => { @@ -20,7 +21,7 @@ describe('translateToServers', () => { href: 'https://www.someotherdomain.com?query=123', }; expect(translateToServers({ host: 'stoplight.io' }, { schemes: ['http'] })).toEqual([ - { url: 'http://stoplight.io' }, + { id: expect.any(String), url: 'http://stoplight.io' }, ]); }); @@ -29,9 +30,11 @@ describe('translateToServers', () => { translateToServers({ host: 'stoplight.io', basePath: '/base-path' }, { schemes: ['http', 'https'] }), ).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io/base-path', }, { + id: expect.any(String), url: 'https://stoplight.io/base-path', }, ]); @@ -49,9 +52,11 @@ describe('translateToServers', () => { ), ).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io/base-path', }, { + id: expect.any(String), url: 'https://stoplight.io/base-path', }, ]); @@ -62,6 +67,7 @@ describe('translateToServers', () => { translateToServers({ schemes: ['https'], host: 'stoplight.io', basePath: '/base-path' }, { schemes: ['http'] }), ).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io/base-path', }, ]); @@ -74,6 +80,7 @@ describe('translateToServers', () => { it('given no basePath should return servers', () => { expect(translateToServers({ schemes: ['http'], host: 'stoplight.io' }, {})).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io', }, ]); @@ -114,9 +121,11 @@ describe('translateToServers', () => { it('should handle invalid server basePath gracefully', () => { expect(translateToServers({ host: 'stoplight.io', basePath: 123 as any }, { schemes: ['http', 'https'] })).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io', }, { + id: expect.any(String), url: 'https://stoplight.io', }, ]); diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index f1cb5cda..c4c00cdf 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -18,11 +18,14 @@ import type { PathParameter, QueryParameter, } from 'swagger-schema-official'; + +import { withContext } from '../../context'; import pickBy = require('lodash.pickby'); import pick = require('lodash.pick'); import { isBoolean, isNonNullable, isString } from '../../guards'; import { Oas2ParamBase } from '../../oas/guards'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; @@ -61,17 +64,19 @@ function chooseQueryParameterStyle( } } -export const translateToHeaderParam: Oas2TranslateFunction< - [param: DeepPartial & Oas2ParamBase & { in: 'header' }], - IHttpHeaderParam -> = function (parameter) { +export const translateToHeaderParam = withContext< + Oas2TranslateFunction<[param: DeepPartial & Oas2ParamBase & { in: 'header' }], IHttpHeaderParam> +>(function (param) { + const name = param.name; + return { + id: this.generateId(`http_header-${this.parentId}-${name}`), + name, style: HttpParamStyles.Simple, - name: parameter.name, - ...buildSchemaForParameter.call(this, parameter), - required: !!parameter.required, + ...buildSchemaForParameter.call(this, param), + required: !!param.required, }; -}; +}); const translateToHeaderParamsFromPair: Oas2TranslateFunction< ArrayCallbackParameters<[name: string, value: unknown]>, @@ -89,39 +94,54 @@ export const translateToHeaderParams: Oas2TranslateFunction<[headers: unknown], return entries(headers).map(translateToHeaderParamsFromPair, this).filter(isNonNullable); }; -export const translateToBodyParameter: Oas2TranslateFunction< - [body: BodyParameter, consumes: string[]], - IHttpOperationRequestBody -> = function (body, consumes) { - const examples = entries(body['x-examples'] || (body.schema ? getExamplesFromSchema(body.schema) : void 0)).map( - ([key, value]) => ({ key, value }), +export const translateToBodyParameter = withContext< + Oas2TranslateFunction<[body: BodyParameter, consumes: string[]], IHttpOperationRequestBody> +>(function (body, consumes) { + const examples = entries(body['x-examples'] || getExamplesFromSchema(body.schema)).map(([key, value]) => + translateToDefaultExample.call(this, key, value), ); - return pickBy({ - description: body.description, - required: body.required, - contents: consumes.map(mediaType => { - return { - mediaType, - schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, - examples, - }; - }), - }); -}; + return { + id: this.generateId(`http_request_body-${this.parentId}`), -export const translateFromFormDataParameters: Oas2TranslateFunction< - [parameters: FormDataParameter[], consumes: string[]], - IHttpOperationRequestBody -> = function (parameters, consumes) { - const finalBody: IHttpOperationRequestBody = { - contents: consumes.map(mediaType => ({ - mediaType, - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', + required: !!body.required, + contents: consumes.map( + withContext(mediaType => { + return { + id: this.generateId(`http_media-${this.parentId}-${mediaType}`), + mediaType, + schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, + examples, + }; + }), + this, + ), + + ...pickBy( + { + description: body.description, }, - })), + isString, + ), + }; +}); + +export const translateFromFormDataParameters = withContext< + Oas2TranslateFunction< + [parameters: (Oas2ParamBase & Partial)[], consumes: string[]], + IHttpOperationRequestBody + > +>(function (parameters, consumes) { + const finalBody: IHttpOperationRequestBody = { + id: this.generateId(`http_request_body-${this.parentId}`), + contents: consumes.map( + withContext(mediaType => ({ + id: this.generateId(`http_media-${this.parentId}-${mediaType}`), + mediaType, + schema: translateSchemaObject.call(this, { type: 'object' }), + })), + this, + ), }; return parameters.reduce((body, parameter) => { @@ -151,9 +171,9 @@ export const translateFromFormDataParameters: Oas2TranslateFunction< }); return body; }, finalBody); -}; +}); -function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { +function buildEncoding(parameter: Oas2ParamBase & Partial): IHttpEncoding | null { switch (parameter.collectionFormat) { case 'csv': return { @@ -183,36 +203,41 @@ function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { return null; } -export const translateToQueryParameter: Oas2TranslateFunction< - [query: DeepPartial & Oas2ParamBase], - IHttpQueryParam -> = function (query) { +export const translateToQueryParameter = withContext< + Oas2TranslateFunction<[query: DeepPartial & Oas2ParamBase], IHttpQueryParam> +>(function (param) { + const name = param.name; + return { - style: chooseQueryParameterStyle(query), - name: query.name, - required: !!query.required, - ...buildSchemaForParameter.call(this, query), + id: this.generateId(`http_query-${this.parentId}-${name}`), + name, + style: chooseQueryParameterStyle(param), + + required: !!param.required, + ...buildSchemaForParameter.call(this, param), ...pickBy( { - allowEmptyValue: query.allowEmptyValue, + allowEmptyValue: param.allowEmptyValue, }, isBoolean, ), }; -}; +}); + +export const translateToPathParameter = withContext< + Oas2TranslateFunction<[param: DeepPartial & Oas2ParamBase], IHttpPathParam> +>(function (param) { + const name = param.name; -export const translateToPathParameter: Oas2TranslateFunction< - [param: DeepPartial & Oas2ParamBase], - IHttpPathParam -> = function (param) { return { - name: param.name, + id: this.generateId(`http_path_param-${this.parentId}-${name}`), + name, style: HttpParamStyles.Simple, required: !!param.required, ...buildSchemaForParameter.call(this, param), }; -}; +}); const buildSchemaForParameter: Oas2TranslateFunction< [param: DeepPartial], diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index cdf894e3..d75b616e 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,12 +1,10 @@ import type { IHttpOperationRequest } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { BodyParameter, FormDataParameter } from 'swagger-schema-official'; import { isNonNullable } from '../../guards'; import { OasVersion } from '../../oas'; -import { getValidOasParameters } from '../../oas/accessors'; +import { createOasParamsIterator } from '../../oas/accessors'; import { getConsumes } from '../accessors'; -import { isHeaderParam, isPathParam, isQueryParam } from '../guards'; +import { isBodyParam, isFormDataParam, isHeaderParam, isPathParam, isQueryParam } from '../guards'; import { Oas2TranslateFunction } from '../types'; import { translateFromFormDataParameters, @@ -15,16 +13,17 @@ import { translateToPathParameter, translateToQueryParameter, } from './params'; +import pickBy = require('lodash.pickby'); +import { Oas2ParamBase } from '../../oas/guards'; + +const iterateOasParams = createOasParamsIterator(OasVersion.OAS2); export const translateToRequest: Oas2TranslateFunction< [path: Record, operation: Record], IHttpOperationRequest > = function (path, operation) { const consumes = getConsumes(this.document, operation); - const parameters = getValidOasParameters(this.document, OasVersion.OAS2, operation.parameters, path.parameters); - - const bodyParameter = parameters.find((p): p is BodyParameter => p.in === 'body'); - const formDataParameters = parameters.filter((p): p is FormDataParameter => p.in === 'formData'); + const parameters = iterateOasParams.call(this, operation, path); const params: Omit, 'body'> = { headers: [], @@ -33,14 +32,8 @@ export const translateToRequest: Oas2TranslateFunction< path: [], }; - let body; - // if 'body' and 'form data' defined prefer 'body' - if (!!bodyParameter) { - // There can be only one body parameter (taking first one) - body = translateToBodyParameter.call(this, bodyParameter, consumes); - } else if (!!formDataParameters.length) { - body = translateFromFormDataParameters.call(this, formDataParameters, consumes); - } + let bodyParameter; + const formDataParameters: (Oas2ParamBase & { in: 'formData' })[] = []; for (const param of parameters) { if (isQueryParam(param)) { @@ -49,9 +42,22 @@ export const translateToRequest: Oas2TranslateFunction< params.path.push(translateToPathParameter.call(this, param)); } else if (isHeaderParam(param)) { params.headers.push(translateToHeaderParam.call(this, param)); + } else if (isBodyParam(param)) { + bodyParameter = translateToBodyParameter.call(this, param, consumes); + } else if (isFormDataParam(param)) { + formDataParameters.push(param); } } + let body; + // if 'body' and 'form data' defined prefer 'body' + if (!!bodyParameter) { + // There can be only one body parameter (taking first one) + body = bodyParameter; + } else if (!!formDataParameters.length) { + body = translateFromFormDataParameters.call(this, formDataParameters, consumes); + } + return { ...params, diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index 34b58bd8..c41ed3a6 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,9 +1,12 @@ import { isPlainObject } from '@stoplight/json'; -import type { IHttpOperationResponse, Optional } from '@stoplight/types'; -import { DeepPartial } from '@stoplight/types'; -import { Operation } from 'swagger-schema-official'; +import type { DeepPartial, IHttpOperationResponse, IMediaTypeContent, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import type { Operation } from 'swagger-schema-official'; +import { withContext } from '../../context'; import { isNonNullable } from '../../guards'; +import { getSharedKey } from '../../oas/resolver'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { entries } from '../../utils'; import { getExamplesFromSchema, getProduces } from '../accessors'; @@ -11,29 +14,40 @@ import { isResponseObject } from '../guards'; import { Oas2TranslateFunction } from '../types'; import { translateToHeaderParams } from './params'; -const translateToResponse: Oas2TranslateFunction< - [produces: string[], statusCode: string, response: unknown], - Optional -> = function (produces, statusCode, response) { +const translateToResponse = withContext< + Oas2TranslateFunction<[produces: string[], statusCode: string, response: unknown], Optional> +>(function (produces, statusCode, response) { const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; + const actualKey = this.context === 'service' ? getSharedKey(resolvedResponse) : statusCode; + const headers = translateToHeaderParams.call(this, resolvedResponse.headers); - const objectifiedExamples = entries( - resolvedResponse.examples || (resolvedResponse.schema ? getExamplesFromSchema(resolvedResponse.schema) : void 0), - ).map(([key, value]) => ({ key, value })); + const objectifiedExamples = entries(resolvedResponse.examples || getExamplesFromSchema(resolvedResponse.schema)).map( + ([key, value]) => translateToDefaultExample.call(this, key, value), + ); const contents = produces - .map(produceElement => ({ - mediaType: produceElement, - schema: isPlainObject(resolvedResponse.schema) - ? translateSchemaObject.call(this, resolvedResponse.schema) - : void 0, - examples: objectifiedExamples.filter(example => example.key === produceElement), - })) + .map }>( + withContext(produceElement => ({ + id: this.generateId(`http_media-${this.parentId}-${produceElement}`), + mediaType: produceElement, + examples: objectifiedExamples.filter(example => example.key === produceElement), + ...pickBy( + { + schema: isPlainObject(resolvedResponse.schema) + ? translateSchemaObject.call(this, resolvedResponse.schema) + : undefined, + }, + isNonNullable, + ), + })), + this, + ) .filter(({ schema, examples }) => !!schema || examples.length > 0); const translatedResponses = { + id: this.generateId(`http_response-${this.parentId}-${actualKey}`), code: statusCode, description: resolvedResponse.description, headers, @@ -44,6 +58,7 @@ const translateToResponse: Oas2TranslateFunction< if (foreignExamples.length > 0) { if (translatedResponses.contents.length === 0) translatedResponses.contents[0] = { + id: this.generateId(`http_media-${this.parentId}-`), mediaType: '', schema: {}, examples: [], @@ -53,7 +68,7 @@ const translateToResponse: Oas2TranslateFunction< } return translatedResponses; -}; +}); export const translateToResponses: Oas2TranslateFunction< [operation: DeepPartial], diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index c7123286..ed674d04 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -1,6 +1,7 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, + HttpSecurityScheme, IApiKeySecurityScheme, IBasicSecurityScheme, IOauth2SecurityScheme, @@ -17,11 +18,11 @@ import type { OAuth2PasswordSecurity, } from 'swagger-schema-official'; +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import { SecurityWithKey } from '../../oas3/accessors'; import { getSecurities } from '../accessors'; import { isSecurityScheme } from '../guards'; -import { Oas2TranslateFunction } from '../types'; +import type { Oas2TranslateFunction } from '../types'; export const translateToFlows: Oas2TranslateFunction<[security: Record], IOauthFlowObjects> = function (security) { @@ -58,60 +59,86 @@ export const translateToFlows: Oas2TranslateFunction<[security: Record & { key: string }], - IBasicSecurityScheme -> = function (security) { +export const translateToBasicSecurityScheme = withContext< + Oas2TranslateFunction<[security: DeepPartial & { key: string }], IBasicSecurityScheme> +>(function (security) { + const key = security.key; + return { + id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'http', scheme: 'basic', - description: security.description, - key: security.key, + key, + + ...pickBy( + { + description: security.description, + }, + isString, + ), }; -}; +}); const ACCEPTABLE_SECURITY_ORIGINS: ApiKeySecurity['in'][] = ['query', 'header']; -export const translateToApiKeySecurityScheme: Oas2TranslateFunction< - [security: DeepPartial & { key: string }], - Optional -> = function (security) { +export const translateToApiKeySecurityScheme = withContext< + Oas2TranslateFunction<[security: DeepPartial & { key: string }], Optional> +>(function (security) { if ('in' in security && security.in && ACCEPTABLE_SECURITY_ORIGINS.includes(security.in)) { + const key = security.key; + return { + id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'apiKey', in: security.in, - name: String(security.name || ''), - description: security.description, - key: security.key, + name: isString(security.name) ? security.name : '', + key, + + ...pickBy( + { + description: security.description, + }, + isString, + ), }; } return; -}; +}); const VALID_OAUTH2_FLOWS = ['implicit', 'password', 'application', 'accessCode']; -export const translateToOauth2SecurityScheme: Oas2TranslateFunction< - [ - security: DeepPartial< - OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity - > & { key: string }, - ], - Optional -> = function (security) { +export const translateToOauth2SecurityScheme = withContext< + Oas2TranslateFunction< + [ + security: DeepPartial< + OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity + > & { key: string }, + ], + Optional + > +>(function (security) { if (!security.flow || !VALID_OAUTH2_FLOWS.includes(security.flow)) return undefined; + const key = security.key; return { + id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'oauth2', flows: translateToFlows.call(this, security), - description: security.description, - key: security.key, + key, + + ...pickBy( + { + description: security.description, + }, + isString, + ), }; -}; +}); export const translateToSingleSecurity: Oas2TranslateFunction< [security: unknown & { key: string }], - Optional + Optional > = function (security) { if (isSecurityScheme(security)) { switch (security.type) { @@ -127,7 +154,7 @@ export const translateToSingleSecurity: Oas2TranslateFunction< return; }; -export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = +export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], HttpSecurityScheme[][]> = function (operationSecurities) { const securities = getSecurities(this.document, operationSecurities); diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index 7be41c5d..af564d5a 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,26 +1,33 @@ import type { IServer, Optional } from '@stoplight/types'; + +import { withContext } from '../../context'; import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../../guards'; +import { ArrayCallbackParameters } from '../../types'; import { isValidScheme } from '../guards'; import type { Oas2TranslateFunction } from '../types'; -export const translateToServers: Oas2TranslateFunction<[operation: Record], IServer[]> = function ( - operation, -) { - let schemes; - if (Array.isArray(operation.schemes)) { - schemes = operation.schemes; - } else if (Array.isArray(this.document.schemes)) { - schemes = this.document.schemes; - } else { - return []; - } - - return schemes.map(translateToServer, this).filter(isNonNullable); -}; - -export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optional> = function (scheme) { +export const translateToServers = withContext], IServer[]>>( + function (operation) { + let schemes; + if (Array.isArray(operation.schemes)) { + schemes = operation.schemes; + this.context = 'operation'; + } else if (Array.isArray(this.document.schemes)) { + schemes = this.document.schemes; + this.context = 'service'; + } else { + return []; + } + + return schemes.map(translateToServer, this).filter(isNonNullable); + }, +); + +export const translateToServer = withContext< + Oas2TranslateFunction, Optional> +>(function (scheme) { const { host } = this.document; if (typeof host !== 'string' || host.length === 0) { return; @@ -39,8 +46,11 @@ export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optiona uri.pathname = basePath; } + const url = uri.toString().replace(/\/$/, ''); // Remove trailing slash + return { - url: uri.toString().replace(/\/$/, ''), // Remove trailing slash + id: this.generateId(`http_server-${this.parentId}-${url}`), + url, ...pickBy( { @@ -49,4 +59,4 @@ export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optiona isString, ), }; -}; +}); diff --git a/src/oas3/__fixtures__/id.json b/src/oas3/__fixtures__/id.json new file mode 100644 index 00000000..cd66100f --- /dev/null +++ b/src/oas3/__fixtures__/id.json @@ -0,0 +1,214 @@ +{ + "openapi": "3.1.0", + "x-stoplight-id": "service_abc", + "info": { + "title": "Users API", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:3000" + } + ], + "tags": [ + { + "name": "mutates" + } + ], + "paths": { + "/users/{userId}": { + "parameters": [ + { + "schema": { + "type": "integer" + }, + "name": "userId", + "in": "path", + "required": true, + "description": "Id of an existing user." + }, + { + "$ref": "#/components/parameters/Some-Header" + } + ], + "get": { + "operationId": "get-user", + "summary": "Get User Info by User ID", + "description": "Retrieve the information of the user with the matching user ID.", + "tags": ["tag-without-root-def"], + "parameters": [ + { + "schema": { + "type": "boolean" + }, + "in": "query", + "name": "summaryOnly" + } + ], + "responses": { + "200": { + "description": "User Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "$ref": "#/components/responses/ErrorResponse" + } + } + }, + "post": { + "operationId": "post-users-userId", + "summary": "Create user", + "tags": ["mutates"], + "parameters": [ + { + "schema": { + "type": "integer" + }, + "name": "Post-Specific-Header", + "in": "header" + } + ], + "security": [ + { + "api-key": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + }, + "examples": { + "basic-example": { + "$ref": "#/components/examples/A-Shared-Example" + } + } + } + } + }, + "responses": { + "201": { + "description": "User Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "$ref": "#/components/responses/ErrorResponse" + } + } + } + } + }, + "components": { + "schemas": { + "User": { + "title": "User", + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "address": { + "$ref": "#/components/schemas/Address" + } + }, + "required": ["id"], + "examples": [] + }, + "Address": { + "title": "Address", + "type": "object", + "properties": { + "street": { + "type": "string" + } + }, + "examples": [ + { + "street": "422 W Riverside Drive" + } + ] + }, + "UserId": { + "type": "number", + "title": "UserId", + "minimum": 0 + }, + "Error": { + "title": "Error", + "type": "object", + "properties": { + "code": { + "type": "number" + }, + "msg": { + "type": "string" + } + }, + "required": ["code", "msg"] + } + }, + "parameters": { + "Some-Header": { + "name": "A-Shared-Header", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + } + }, + "examples": { + "A-Shared-Example": { + "value": { + "id": 0, + "address": { + "street": "string" + } + } + } + }, + "securitySchemes": { + "api-key": { + "name": "API Key", + "type": "apiKey", + "in": "query" + } + }, + "responses": { + "ErrorResponse": { + "description": "A generic error response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + } +} diff --git a/src/oas3/__fixtures__/output.ts b/src/oas3/__fixtures__/output.ts new file mode 100644 index 00000000..54bd1fc8 --- /dev/null +++ b/src/oas3/__fixtures__/output.ts @@ -0,0 +1,489 @@ +/** + NOTE that if any object anywhere ever has an `x-stoplight-id` on it, prefer that + over calling the generate function. + + Used https://md5calc.com/hash/fnv1a32 to hash the ids. + */ +export default [ + /** + * The http_service + */ + { + // hash(document id - end user needs to be able to customize this..) + // by default could hash("#")? + // this example has a x-stoplight-id prop on the root though, so using that + id: 'service_abc', + version: '1.0', + name: 'Users API', + servers: [ + { + // hash(`http_server-${parentId}-${server.url}`) + // closest parent with an id is the service, so ends up being... + // hash('http_server-service_abc-http://localhost:3000') + id: '98ddf8a4b5bdc', + name: 'Users API', + url: 'http://localhost:3000', + }, + ], + securitySchemes: [ + { + // hash(`http_security-${parentId}-${security.key || security.name}`) + // closest parent with an id is the service, so ends up being... + // hash('http_security-service_abc-api-key') + id: '202a905f9dff6', + key: 'api-key', + type: 'apiKey', + name: 'API Key', + in: 'query', + }, + ], + tags: [ + { + // hash(`tag-${serviceId}-${tag.name}`) + // always generate tags based on the serviceId, so ends up being... + // hash('tag-service_abc-mutates') + id: '936737e88c6fa', + name: 'mutates', + }, + ], + }, + + /** + * http_operation 1 of 2 (the GET operation) + */ + { + // hash(`http_operation-${parentId}-${method}-${pathWithParamNamesEmpty}`) + // for pathWithParamNamesEmpty, remove all characters between {} segments + // closest parent with an id is the service, so ends up being... + // hash('http_operation-service_abc-get-/users/{}') + id: '96043a63b6901', + iid: 'get-user', + description: 'Retrieve the information of the user with the matching user ID.', + method: 'get', + path: '/users/{userId}', + summary: 'Get User Info by User ID', + deprecated: false, + internal: false, + responses: [ + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // closest parent with an id is the operation, so ends up being... + // hash('http_response-96043a63b6901-200') + id: 'f387e16c7d39d', + code: '200', + description: 'User Found', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-f387e16c7d39d-application/json') + id: 'fce50f391bf57', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de4f083463b7c', + title: 'User', + type: 'object', + properties: { + id: { + type: 'integer', + readOnly: true, + }, + address: { + // @TODO + $ref: '#/components/schemas/Address', + }, + }, + required: ['id'], + examples: [], + }, + examples: [], + encodings: [], + }, + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-f387e16c7d39d-application/xml') + id: '48eeb3ee2a049', + mediaType: 'application/xml', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': '069dfbb6c6315', + type: 'string', + }, + examples: [], + encodings: [], + }, + ], + }, + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // This response was defined in shared components originally.. the closest parent with an + // id is the service, and the key was ErrorResponse so ends up being... + // hash('http_response-service_abc-ErrorResponse') + id: '437771f63f179', + code: '404', + description: 'A generic error response.', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-437771f63f179-application/json') + id: '4d98be34f341a', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': '2691eb0db9efc', + type: 'object', + properties: { + error: { + // @TODO + $ref: '#/components/schemas/Error', + }, + }, + }, + examples: [], + encodings: [], + }, + ], + }, + ], + servers: [ + { + // hash(`http_server-${parentId}-${server.url}`) + // this is coming from the service defined servers (rather than being defined specifically for this one operation) + // so the ID ends up being the same as the service defined one... look in file for "98ddf8a4b5bdc" to find the other def above + id: '98ddf8a4b5bdc', + name: 'Users API', + url: 'http://localhost:3000', + }, + ], + request: { + // Request doesn't need an id + body: { + id: 'd5027559477f8', + // Really this doesn't even need to be here... there is no request body for this op + contents: [], + }, + headers: [ + { + // hash(`http_header-${parentId}-${param.name}`) + // This header was defined in shared components originally, note how this ends up appearing several times in this doc. + // The closest parent with an id is the service, so ends up being... + // hash('http_header-service_abc-A-Shared-Header') + id: '21b1f96bd26ee', + name: 'A-Shared-Header', + required: false, + deprecated: false, + explode: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'be6b513de1b69', + type: 'string', + }, + examples: [], + }, + ], + query: [ + { + // hash(`http_query-${parentId}-${param.name}`) + // This was defined directly on the operation (not a shared component), so the closest + // parent with an id is the operation, so ends up being... + // hash('http_query-96043a63b6901-summaryOnly') + id: 'efe9534d001fc', + name: 'summaryOnly', + required: false, + deprecated: false, + explode: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'boolean', + 'x-stoplight-id': 'aca62504578bd', + }, + examples: [], + }, + ], + cookie: [], + path: [ + { + // hash(`http_path_param-${parentId}-${param.name}`) + // This was defined on the path, so we use the path to generate the id (thus if another operation on this path was in this doc, it would have path param with same id) + // path's id = hash(`http_path-${parentId}-${path}`) + // The closest parent id to a path, is the service, so this equals... (remember that path segments have characters removed, since they are basically meaningless) + // hash('http_path-service_abc-/users/{}') = '05574f79' + // and then the final path param id... + // hash('http_path_param-05574f79-userId') = 'fe171ec8cfd0b' + id: 'fe171ec8cfd0b', + name: 'userId', + required: true, + description: 'Id of an existing user.', + explode: false, + deprecated: false, + style: 'simple', + schema: { + 'x-stoplight-id': '13ad531bed72e', + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'integer', + }, + examples: [], + }, + ], + }, + tags: [ + { + // hash(`tag-${serviceId}-${tag.name}`) + // always generate tags based on the serviceId, so ends up being... + // hash('tag-service_abc-tag-without-root-def') + id: '9862017e672e6', + name: 'tag-without-root-def', + }, + ], + security: [], + extensions: {}, + }, + + /** + * http_operation 2 of 2 (the POST operation) + */ + { + // Same process as first time... and yes, I know "POST" doesn't make sense on this path lol + id: 'b16a96d287951', + iid: 'post-users-userId', + method: 'post', + path: '/users/{userId}', + summary: 'Create user', + deprecated: false, + internal: false, + responses: [ + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // closest parent with an id is the operation, so ends up being... + // hash('http_response-b16a96d287951-201') + id: 'd8ca38606ee5d', + code: '201', + description: 'User Created', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-d8ca38606ee5d-application/json') + id: '88460a8f1a612', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de4f083463b7c', + title: 'User', + type: 'object', + properties: { + id: { + type: 'integer', + readOnly: true, + }, + address: { + // @TODO + $ref: '#/components/schemas/Address', + }, + }, + required: ['id'], + examples: [], + }, + examples: [], + encodings: [], + }, + ], + }, + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // This response was defined in shared components originally.. the closest parent with an + // id is the service, and the key was ErrorResponse so ends up being... + // hash('http_response-service_abc-ErrorResponse') + // NOTE how this ID is the same as the 404 response from the get user operation... + id: '437771f63f179', + code: '400', + description: 'A generic error response.', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-437771f63f179-application/json') + // NOTE how this ID is the same as the 404 response json media type from the get user operation... + id: '4d98be34f341a', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + // hash(`schema-${parentId}-${key}`) + // This schema was defined in shared components originally.. the closest parent with an + // id is the service, and the key was ErrorResponse so ends up being... + // hash('schema-4d98be34f341a-ErrorResponse') + 'x-stoplight-id': '2691eb0db9efc', + type: 'object', + properties: { + error: { + // @TODO + $ref: '#/components/schemas/Error', + }, + }, + }, + examples: [], + encodings: [], + }, + ], + }, + ], + servers: [ + { + // hash(`http_server-${parentId}-${server.url}`) + // this is coming from the service defined servers (rather than being defined specifically for this one operation) + // so the ID ends up being the same as the service defined one (and also present in the get user op)... + // look in file for "98ddf8a4b5bdc" to find the other defs above + id: '98ddf8a4b5bdc', + name: 'Users API', + url: 'http://localhost:3000', + }, + ], + request: { + body: { + // hash(`http_request_body-${parentId}`) + // closest parent with an id is the operation, so ends up being... + // hash('http_request_body-b16a96d287951') + id: '07267ec331fc9', + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the request body, so ends up being... + // hash('http_media-07267ec331fc9-application/json') + id: '00db77c676e1a', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de4f083463b7c', + title: 'User', + type: 'object', + properties: { + id: { + type: 'integer', + readOnly: true, + }, + address: { + // @TODO + $ref: '#/components/schemas/Address', + }, + }, + required: ['id'], + examples: [], + }, + examples: [ + { + // hash(`example-${parentId}-${example key}`) + // This example was defined in shared components originally.. the closest parent with an + // id is the service, and the key was "A-Shared-Example" so ends up being... + // hash('example-service_abc-A-Shared-Example') + id: '5a69041e065b0', + key: 'basic-example', + value: { + id: 0, + address: { + street: 'string', + }, + }, + }, + ], + encodings: [], + }, + ], + }, + headers: [ + { + // hash(`http_header-${parentId}-${param.name}`) + // This was defined directly on the operation (not a shared component), so the closest + // parent with an id is the operation, so ends up being... + // hash('http_header-b16a96d287951-Post-Specific-Header') + id: '1ead595922478', + name: 'Post-Specific-Header', + required: false, + deprecated: false, + explode: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de5897f178a5d', + type: 'integer', + }, + examples: [], + }, + { + // hash(`http_header-${parentId}-${param.name}`) + // This header was defined in shared components originally, note how this ends up appearing several times in this doc. + // The closest parent with an id is the service, so ends up being... + // hash('http_header-service_abc-A-Shared-Header') + id: '21b1f96bd26ee', + name: 'A-Shared-Header', + required: false, + deprecated: false, + explode: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'be6b513de1b69', + type: 'string', + }, + examples: [], + }, + ], + query: [], + cookie: [], + path: [ + { + // Same process as other path param, resulting in the same + // ID (so this path param node will end up as single instance in the graph, with an edge from each operation pointing at it) + id: 'fe171ec8cfd0b', + name: 'userId', + required: true, + description: 'Id of an existing user.', + deprecated: false, + explode: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + // hash(`http_media-${parentId}-${key}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-437771f63f179-application/json') + 'x-stoplight-id': '13ad531bed72e', + type: 'integer', + }, + examples: [], + }, + ], + }, + tags: [ + { + // hash(`tag-${serviceId}-${tag.name}`) + // always generate tags based on the serviceId, so ends up being... + // hash('tag-service_abc-mutates') + id: '936737e88c6fa', + name: 'mutates', + }, + ], + security: [ + [ + { + // This is effectively a silly "fake" ref that openapi pulls... so + // we can effectively just re-use the same ID for the relevant securityScheme + // from the root.. note the ID is the same as the root securityScheme id + id: '202a905f9dff6', + key: 'api-key', + type: 'apiKey', + name: 'API Key', + in: 'query', + }, + ], + ], + extensions: {}, + }, +]; diff --git a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap index 92aa3bb3..bd8c38a7 100644 --- a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap @@ -5,14 +5,17 @@ Object { "callbacks": Array [ Object { "callbackName": "myCallback", + "deprecated": false, "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "cbId", + "internal": false, "method": "post", "path": "http://example.com?transactionId={$request.body#/id}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -25,9 +28,11 @@ Object { "tags": Array [], }, ], + "deprecated": false, "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", + "internal": false, "method": "post", "path": "/subscribe", "request": Object { @@ -36,6 +41,7 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -45,9 +51,11 @@ Object { }, }, "type": "object", + "x-stoplight-id": Any, }, }, ], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -66,13 +74,15 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", + "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -92,13 +102,15 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", + "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -109,6 +121,7 @@ Object { "security": Array [], "servers": Array [ Object { + "id": Any, "name": "title", "url": "operation/server", }, @@ -123,13 +136,15 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", + "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -140,6 +155,7 @@ Object { "security": Array [], "servers": Array [ Object { + "id": Any, "name": "title", "url": "path/server", }, @@ -154,13 +170,15 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", + "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -173,6 +191,7 @@ Object { "summary": "summary", "tags": Array [ Object { + "id": Any, "name": "tag1", }, ], @@ -184,13 +203,15 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", + "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -201,6 +222,7 @@ Object { "security": Array [], "servers": Array [ Object { + "id": Any, "name": "title", "url": "spec/server", }, diff --git a/src/oas3/__tests__/accessors.test.ts b/src/oas3/__tests__/accessors.test.ts index 5ccb31d5..4b0e7249 100644 --- a/src/oas3/__tests__/accessors.test.ts +++ b/src/oas3/__tests__/accessors.test.ts @@ -1,4 +1,10 @@ -import { getSecurities } from '../accessors'; +import { DeepPartial } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; + +import { getSecurities as _getSecurities, OperationSecurities } from '../accessors'; + +const getSecurities = (document: DeepPartial, operationSecurities?: OperationSecurities) => + _getSecurities(document, operationSecurities); describe('getOas3Securities', () => { it('given no global securities should return empty array', () => { @@ -33,10 +39,12 @@ describe('getOas3Securities', () => { }), ).toStrictEqual([ [ - { - type: 'apiKey', - key: 'operationScheme', - }, + [ + 'operationScheme', + { + type: 'apiKey', + }, + ], ], ]); }); @@ -57,10 +65,12 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([ [ - { - type: 'apiKey', - key: 'specScheme', - }, + [ + 'specScheme', + { + type: 'apiKey', + }, + ], ], ]); }); @@ -85,10 +95,12 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([ [ - { - type: 'apiKey', - key: 'specScheme', - }, + [ + 'specScheme', + { + type: 'apiKey', + }, + ], ], ]); }); @@ -113,62 +125,4 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([[]]); }); - - it('should return security for each scope', () => { - const res = getSecurities( - { - components: { - securitySchemes: { - authWith2Scopes: { - type: 'oauth2', - flows: { - authorizationCode: { - scopes: { - accessToken: 'accessToken description', - secScope: 'secScope description', - }, - }, - }, - }, - }, - }, - security: [ - { - authWith2Scopes: ['accessToken', 'secScope'], - }, - ], - }, - [ - { - authWith2Scopes: ['accessToken'], - }, - { authWith2Scopes: ['secScope'] }, - ], - ); - - expect(res).toStrictEqual([ - [ - { - type: 'oauth2', - flows: { - authorizationCode: { - scopes: { accessToken: 'accessToken description' }, - }, - }, - key: 'authWith2Scopes', - }, - ], - [ - { - type: 'oauth2', - flows: { - authorizationCode: { - scopes: { secScope: 'secScope description' }, - }, - }, - key: 'authWith2Scopes', - }, - ], - ]); - }); }); diff --git a/src/oas3/__tests__/ids.test.ts b/src/oas3/__tests__/ids.test.ts new file mode 100644 index 00000000..7aa3126b --- /dev/null +++ b/src/oas3/__tests__/ids.test.ts @@ -0,0 +1,12 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { transformOas3Operations } from '../operation'; +import { transformOas3Service } from '../service'; + +test('should generate proper ids', async () => { + const document = JSON.parse(await fs.promises.readFile(path.join(__dirname, '../__fixtures__/id.json'), 'utf8')); + const { default: output } = await import('../__fixtures__/output'); + + expect([transformOas3Service({ document }), ...transformOas3Operations(document)]).toEqual(output); +}); diff --git a/src/oas3/__tests__/operation.test.ts b/src/oas3/__tests__/operation.test.ts index fdba8752..56a11075 100644 --- a/src/oas3/__tests__/operation.test.ts +++ b/src/oas3/__tests__/operation.test.ts @@ -1,6 +1,13 @@ import { OpenAPIObject } from 'openapi3-ts'; -import { transformOas3Operation, transformOas3Operations } from '../operation'; +import { + transformOas3Operation as _transformOas3Operation, + transformOas3Operations as _transformOas3Operations, +} from '../operation'; + +const transformOas3Operation: typeof _transformOas3Operation = opts => _transformOas3Operation(opts); + +const transformOas3Operations: typeof _transformOas3Operations = opts => _transformOas3Operations(opts); describe('transformOas3Operation', () => { it('should return deprecated property in http operation root', () => { @@ -85,7 +92,7 @@ describe('transformOas3Operation', () => { }, }; - expect(transformOas3Operations(document)).toStrictEqual([ + expect(transformOas3Operations(document)).toEqual([ expect.objectContaining({ path: '/users/{userId}', method: 'get', @@ -98,8 +105,7 @@ describe('transformOas3Operation', () => { internal: false, extensions: {}, }), - { - id: '?http-operation-id?', + expect.objectContaining({ path: '/users/{userId}', method: 'put', request: expect.any(Object), @@ -108,7 +114,7 @@ describe('transformOas3Operation', () => { servers: expect.any(Array), tags: [], extensions: {}, - }, + }), ]); }); @@ -138,7 +144,14 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + }); }); it('given some tags should translate operation with those tags', () => { @@ -174,7 +187,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + tags: [ + { + id: expect.any(String), + }, + ], + }); }); it('given invalid tags should translate operation as there were no tags specified', () => { @@ -236,12 +261,15 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('tags', [ { + id: expect.any(String), name: '2', }, { + id: expect.any(String), name: 'test', }, { + id: expect.any(String), name: 'false', }, ]); @@ -274,7 +302,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + servers: [ + { + id: expect.any(String), + }, + ], + }); }); it.each([2, '', null, [null]])( @@ -339,6 +379,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { + id: expect.any(String), name: 'title', url: 'operation/server', }, @@ -372,7 +413,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + servers: [ + { + id: expect.any(String), + }, + ], + }); }); it.each([2, '', null, [null]])( @@ -437,6 +490,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { + id: expect.any(String), name: 'title', url: 'path/server', }, @@ -470,7 +524,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + servers: [ + { + id: expect.any(String), + }, + ], + }); }); it.each([2, '', null, [null]])( @@ -535,6 +601,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { + id: expect.any(String), name: 'title', url: 'spec/server', }, @@ -588,7 +655,32 @@ describe('transformOas3Operation', () => { method: 'post', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + }, + callbacks: [ + { + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + }, + ], + }); }); it('given malformed parameters should translate operation with those parameters', () => { @@ -625,18 +717,23 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/users/{userId}', + deprecated: false, + internal: false, request: { body: { + id: expect.any(String), contents: [], }, cookie: [], headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'test', }, @@ -644,8 +741,10 @@ describe('transformOas3Operation', () => { name: 'name', deprecated: false, explode: false, + required: false, style: 'simple', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', format: 'int32', @@ -705,27 +804,34 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/users/{userId}', + deprecated: false, + internal: false, request: { body: { + id: expect.any(String), contents: [], }, cookie: [], headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'some example', }, ], + required: false, deprecated: false, explode: false, style: 'simple', name: 'name', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', format: 'int32', @@ -748,7 +854,6 @@ describe('transformOas3Operation', () => { it('should keep the server variables', () => { const document: Partial = { - id: '?http-service-id?', paths: { '/pets': { get: { @@ -788,6 +893,7 @@ describe('transformOas3Operation', () => { expect(transformOas3Operation({ document, path: '/pets', method: 'get' }).servers).toEqual([ { + id: expect.any(String), description: 'Sample Petstore Server Https', url: 'https://petstore.swagger.io/v2', variables: { @@ -805,6 +911,7 @@ describe('transformOas3Operation', () => { }, }, { + id: expect.any(String), description: 'Sample Petstore Server Http', url: 'http://petstore.swagger.io/v2', }, @@ -891,16 +998,21 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/pet', + deprecated: false, + internal: false, request: { body: { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [ { + id: expect.any(String), key: 'pet-shared', value: { type: 'object', @@ -913,6 +1025,7 @@ describe('transformOas3Operation', () => { }, }, { + id: expect.any(String), key: 'pet-not-shared', value: { 'not-shared': true, @@ -921,6 +1034,7 @@ describe('transformOas3Operation', () => { ], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: {}, @@ -935,12 +1049,15 @@ describe('transformOas3Operation', () => { }, responses: [ { + id: expect.any(String), code: '200', contents: [ { + id: expect.any(String), encodings: [], examples: [ { + id: expect.any(String), key: 'pet-shared', value: { properties: { @@ -953,6 +1070,7 @@ describe('transformOas3Operation', () => { }, }, { + id: expect.any(String), key: 'pet-not-shared', value: { 'not-shared': true, @@ -961,6 +1079,7 @@ describe('transformOas3Operation', () => { ], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: {}, @@ -1018,12 +1137,15 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -1080,12 +1202,15 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -1128,9 +1253,12 @@ describe('transformOas3Operation', () => { method: 'post', document, }), - ).toHaveProperty('request.body', { - contents: [], - }); + ).toHaveProperty( + 'request.body', + expect.objectContaining({ + contents: [], + }), + ); }); describe('OAS 3.1 support', () => { @@ -1211,22 +1339,20 @@ describe('transformOas3Operation', () => { ).toEqual( expect.objectContaining({ responses: [ - { + expect.objectContaining({ code: '200', contents: [ - { - encodings: [], - examples: [], - mediaType: 'application/json', + expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }, + }), ], headers: [], - }, + }), ], }), ); @@ -1240,21 +1366,23 @@ describe('transformOas3Operation', () => { ).toEqual( expect.objectContaining({ request: { - body: { + body: expect.objectContaining({ contents: [ expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, }), ], - }, + }), cookie: [], headers: [ expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'string', format: 'email', @@ -1266,19 +1394,19 @@ describe('transformOas3Operation', () => { query: [], }, responses: [ - { + expect.objectContaining({ code: '200', contents: [ expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, }), ], - headers: [], - }, + }), ], }), ); @@ -1322,6 +1450,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('responses', [ { + id: expect.any(String), code: '200', contents: expect.any(Array), headers: expect.any(Array), @@ -1331,6 +1460,7 @@ describe('transformOas3Operation', () => { it('should support requestBodies on any method', () => { const document: Partial = { + 'x-stoplight-id': 'abc-def', openapi: '3.1.0', paths: { '/subscribe': { @@ -1363,12 +1493,15 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { diff --git a/src/oas3/__tests__/service.test.ts b/src/oas3/__tests__/service.test.ts index 523ac541..06eb125f 100644 --- a/src/oas3/__tests__/service.test.ts +++ b/src/oas3/__tests__/service.test.ts @@ -1,6 +1,15 @@ import { OpenAPIObject } from 'openapi3-ts'; -import { transformOas3Service } from '../service'; +import { transformOas3Service as _transformOas3Service } from '../service'; + +const transformOas3Service: typeof _transformOas3Service = ({ document, ...opts }) => + _transformOas3Service({ + document: { + 'x-stoplight-id': 'abc', + ...document, + }, + ...opts, + }); describe('oas3 service', () => { it('should handle non object security objects', () => { @@ -16,11 +25,12 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', securitySchemes: [ { + id: expect.any(String), key: 't1', type: 'apiKey', in: undefined, @@ -68,11 +78,12 @@ describe('oas3 service', () => { }, ])('should handle lacking flows for oauth2 security object', document => { expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', securitySchemes: [ { + id: expect.any(String), flows: {}, key: 't1', type: 'oauth2', @@ -87,7 +98,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', }); @@ -110,15 +121,17 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', servers: [ { + id: expect.any(String), name: '', url: 'https://petstore.swagger.io/v2', }, { + id: expect.any(String), name: '', url: 'http://petstore.swagger.io/v2', }, @@ -135,7 +148,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', }); @@ -143,7 +156,7 @@ describe('oas3 service', () => { it('should handle server variables', () => { const document: Partial = { - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', servers: [ @@ -172,11 +185,12 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', servers: [ { + id: expect.any(String), description: 'Sample Petstore Server Https', url: 'https://petstore.swagger.io/v2', variables: { @@ -194,6 +208,7 @@ describe('oas3 service', () => { }, }, { + id: expect.any(String), description: 'Sample Petstore Server Http', url: 'http://petstore.swagger.io/v2', }, @@ -240,7 +255,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', summary: 'Very cool API', @@ -260,7 +275,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', license: { @@ -286,7 +301,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '1.0.0', logo: { @@ -310,7 +325,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', contact: { url: 'https://stoplight.io', diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index 38f66cf5..79ea47bb 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,46 +1,50 @@ import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial, Dictionary, HttpSecurityScheme } from '@stoplight/types'; +import type { DeepPartial, Dictionary, HttpSecurityScheme, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); import type { OpenAPIObject } from 'openapi3-ts'; +import { isNonNullable } from '../guards'; import { entries } from '../utils'; -import { isSecurityScheme, isSecuritySchemeWithKey } from './guards'; +import { isSecurityScheme } from './guards'; export type OperationSecurities = Dictionary[] | undefined; -export type SecurityWithKey = HttpSecurityScheme & { key: string }; export function getSecurities( document: DeepPartial, operationSecurities?: unknown, -): SecurityWithKey[][] { +): [key: string, security: Omit][][] { const definitions = document.components?.securitySchemes; if (!isPlainObject(definitions)) return []; return (Array.isArray(operationSecurities) ? operationSecurities : document.security || []).map(operationSecurity => { return entries(operationSecurity) - .map(([opScheme, scopes]) => { + .map]>>(([opScheme, scopes]) => { const definition = definitions[opScheme]; - if (isSecurityScheme(definition) && definition.type === 'oauth2') { + if (!isSecurityScheme(definition)) return; + + if (definition.type === 'oauth2') { // Put back only the flows that are part of the current definition - return { - ...definition, - flows: Object.fromEntries( - entries(definition.flows).map(([name, flow]) => [ - name, - { - ...flow, - scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), - }, - ]), - ), - key: opScheme, - }; + return [ + opScheme, + { + ...definition, + flows: Object.fromEntries( + entries(definition.flows).map(([name, flow]) => [ + name, + { + ...flow, + scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), + }, + ]), + ), + }, + ]; } - return { ...definition, key: opScheme }; + return [opScheme, definition]; }) - .filter(isSecuritySchemeWithKey); + .filter(isNonNullable); }); } diff --git a/src/oas3/guards.ts b/src/oas3/guards.ts index 3bac2280..4c964fab 100644 --- a/src/oas3/guards.ts +++ b/src/oas3/guards.ts @@ -11,14 +11,9 @@ import type { TagObject, } from 'openapi3-ts'; -import type { SecurityWithKey } from './accessors'; - export const isSecurityScheme = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecuritySchemeObject => isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; -export const isSecuritySchemeWithKey = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecurityWithKey => - isSecurityScheme(maybeSecurityScheme) && typeof maybeSecurityScheme.key === 'string'; - export const isBaseParameterObject = ( maybeBaseParameterObject: unknown, ): maybeBaseParameterObject is BaseParameterObject => diff --git a/src/oas3/index.ts b/src/oas3/index.ts index 2e002f33..f2e98a7a 100644 --- a/src/oas3/index.ts +++ b/src/oas3/index.ts @@ -1,3 +1,2 @@ export * from './operation'; export * from './service'; -export * from './transformers/request'; diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index f423a26c..ca08e7ca 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,54 +1,42 @@ -import { IHttpOperation } from '@stoplight/types'; -import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; +import type { DeepPartial, IHttpOperation } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import type { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../context'; import { isNonNullable } from '../guards'; -import { transformOasOperations } from '../oas'; -import { getExtensions } from '../oas/accessors'; -import { translateToTags } from '../oas/tags'; +import { transformOasOperation, transformOasOperations } from '../oas'; +import { resolveRef } from '../oas/resolver'; import type { Oas3HttpOperationTransformer } from '../oas/types'; -import { maybeResolveLocalRef } from '../utils'; +import { Fragment } from '../types'; import { translateToCallbacks } from './transformers/callbacks'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; import { translateToServers } from './transformers/servers'; -export function transformOas3Operations(document: OpenAPIObject): IHttpOperation[] { +export function transformOas3Operations(document: DeepPartial): IHttpOperation[] { return transformOasOperations(document, transformOas3Operation); } -export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]) as PathsObject; - if (typeof pathObj !== 'object' || pathObj === null) { - throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); - } +export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document: _document, path, method }) => { + const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); + const httpOperation = transformOasOperation.call(ctx, path, method); + const pathObj = ctx.maybeResolveLocalRef(ctx.document.paths![path]) as Fragment; + const operation = ctx.maybeResolveLocalRef(pathObj[method]) as Fragment; - const operation = maybeResolveLocalRef(document, pathObj[method]) as OperationObject; - if (!operation) { - throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); - } + return { + ...httpOperation, - const ctx = createContext(document); - - const httpOperation: IHttpOperation = { - id: '?http-operation-id?', - iid: operation.operationId, - description: operation.description, - deprecated: operation.deprecated, - internal: operation['x-internal'], - method, - path, - summary: operation.summary, responses: translateToResponses.call(ctx, operation.responses), request: translateToRequest.call(ctx, pathObj, operation), - callbacks: translateToCallbacks.call(ctx, operation.callbacks), - tags: translateToTags.call(ctx, operation.tags), security: translateToSecurities.call(ctx, operation.security), - extensions: getExtensions(operation), servers: translateToServers.call(ctx, pathObj, operation), - }; - return pickBy(httpOperation, isNonNullable) as IHttpOperation; + ...pickBy( + { + callbacks: translateToCallbacks.call(ctx, operation.callbacks), + }, + isNonNullable, + ), + }; }; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index acce098f..27a8dcdb 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,20 +1,22 @@ import { isPlainObject } from '@stoplight/json'; -import type { Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; +import type { HttpSecurityScheme, Optional } from '@stoplight/types'; + +import { createContext, DEFAULT_ID_GENERATOR, withContext } from '../context'; import { isNonNullable } from '../guards'; +import { resolveRef } from '../oas/resolver'; import { transformOasService } from '../oas/service'; import type { Oas3HttpServiceTransformer } from '../oas/types'; import { ArrayCallbackParameters } from '../types'; import { entries } from '../utils'; -import { SecurityWithKey } from './accessors'; import { isSecurityScheme } from './guards'; import { translateToSingleSecurity } from './transformers/securities'; import { translateToServer } from './transformers/servers'; import { Oas3TranslateFunction } from './types'; +import pickBy = require('lodash.pickby'); -export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document); +export const transformOas3Service: Oas3HttpServiceTransformer = ({ document: _document }) => { + const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); + const { document } = ctx; const httpService = transformOasService.call(ctx); if (typeof document.info?.summary === 'string') { @@ -84,16 +86,10 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = return httpService; }; -const translateSecurityScheme: Oas3TranslateFunction< - ArrayCallbackParameters<[name: string, scheme: unknown]>, - Optional -> = function ([key, definition]) { +const translateSecurityScheme = withContext< + Oas3TranslateFunction, Optional> +>(function ([key, definition]) { if (!isSecurityScheme(definition)) return; - const transformed = translateToSingleSecurity.call(this, definition); - if (transformed && 'key' in transformed) { - transformed.key = key; - } - - return transformed; -}; + return translateToSingleSecurity.call(this, [key, definition]); +}); diff --git a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap index 04e8e103..aa6e45c8 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap @@ -10,20 +10,24 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "a", "summary": "example summary", "value": "hey", }, Object { + "id": Any, "key": "__default", "value": Object {}, }, ], "explode": true, + "id": Any, "name": "header-name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, "style": "simple", } @@ -43,12 +47,14 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "a", "summary": "example summary", "value": "hey", }, ], "explode": false, + "id": Any, "name": "h1", "style": "simple", }, @@ -60,14 +66,17 @@ Object { ], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -82,6 +91,7 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "name": "h1", "style": "simple", }, @@ -97,6 +107,7 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "name": "h1", "style": "simple", }, @@ -108,14 +119,17 @@ Object { ], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -125,14 +139,17 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -142,13 +159,16 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "default", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -166,14 +186,17 @@ Object { ], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; diff --git a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap index 4afeb24b..afd99f82 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -4,9 +4,20 @@ exports[`translateOas3ToRequest give a request body should translate it 1`] = ` Object { "body": Object { "contents": Array [ - "translateMediaTypeObject([\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}], 0, [[\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}]])", + Object { + "encodings": Array [], + "examples": Array [], + "id": Any, + "mediaType": "content-a", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "deprecated": true, + "x-stoplight-id": Any, + }, + }, ], "description": "descr", + "id": Any, "required": true, }, "cookie": Array [], @@ -20,6 +31,7 @@ exports[`translateOas3ToRequest given no request body should translate parameter Object { "body": Object { "contents": Array [], + "id": "c27fe9940305e", }, "cookie": Array [], "headers": Array [ @@ -33,7 +45,9 @@ Object { "description": "descr", "examples": Array [], "explode": false, + "id": "824b189f6d434", "name": "param-name-3", + "required": false, "style": "simple", }, ], @@ -49,7 +63,9 @@ Object { "description": "descr", "examples": Array [], "explode": false, + "id": "7e5f6c5bf0edf", "name": "param-name-2", + "required": false, "style": "simple", }, Object { @@ -62,7 +78,9 @@ Object { "description": "descr", "examples": Array [], "explode": false, + "id": "7e8f6c5bf0b36", "name": "param-name-1", + "required": false, "style": "simple", }, ], diff --git a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap index 54946b4f..e4df9bec 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -1,84 +1,97 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`translateToOas3Responses given a response in dictionary should translate 1`] = ` -Array [ - Object { - "code": "200", - "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [ - Object { - "key": "default", - "value": "dumb", - }, - ], - "mediaType": "fake-content-type-200", - }, - ], - "description": "descr 200", - "headers": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "name": "fake-header-name-200", - "style": "simple", - }, - ], - }, - Object { - "code": "default", - "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "mediaType": "fake-content-type", - }, - ], - "description": "descr", - "headers": Array [ - Object { - "description": "calls per hour allowed by the user", - "encodings": Array [], - "examples": Array [ - Object { - "key": "__default", - "value": 1000, - }, - ], - "explode": false, - "name": "fake-header-name-1", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": "integer", +Object { + "code": "200", + "contents": Array [ + Object { + "encodings": Array [], + "examples": Array [ + Object { + "id": Any, + "key": "default", + "value": "dumb", + }, + ], + "id": Any, + "mediaType": "fake-content-type-200", + }, + ], + "description": "descr 200", + "headers": Array [ + Object { + "encodings": Array [], + "examples": Array [], + "id": Any, + "name": "fake-header-name-200", + "style": "simple", + }, + ], + "id": Any, +} +`; + +exports[`translateToOas3Responses given a response in dictionary should translate 2`] = ` +Object { + "code": "default", + "contents": Array [ + Object { + "encodings": Array [], + "examples": Array [], + "id": Any, + "mediaType": "fake-content-type", + }, + ], + "description": "descr", + "headers": Array [ + Object { + "description": "calls per hour allowed by the user", + "encodings": Array [], + "examples": Array [ + Object { + "id": Any, + "key": "__default", + "value": 1000, }, - "style": "simple", + ], + "explode": false, + "id": Any, + "name": "fake-header-name-1", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer", + "x-stoplight-id": Any, }, - Object { - "description": "calls per hour allowed by the user", - "encodings": Array [], - "examples": Array [ - Object { - "key": "__default", - "value": 1000, - }, - ], - "explode": false, - "name": "fake-header-name-2", - "required": true, - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": "integer", + "style": "simple", + }, + Object { + "description": "calls per hour allowed by the user", + "encodings": Array [], + "examples": Array [ + Object { + "id": Any, + "key": "__default", + "value": 1000, }, - "style": "simple", + ], + "explode": false, + "id": Any, + "name": "fake-header-name-2", + "required": true, + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer", + "x-stoplight-id": Any, }, - ], - }, -] + "style": "simple", + }, + ], + "id": Any, +} `; diff --git a/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap index 94ef254f..7bb51f80 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap @@ -4,6 +4,7 @@ exports[`translateToServers translate single ServerObject to IServer 1`] = ` Array [ Object { "description": "description", + "id": "6f520f1ba667c", "url": "http://stoplight.io/path", "variables": Object { "a": Object { diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index d22bf44a..47017ebd 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -1,16 +1,17 @@ import type { SchemaObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateHeaderObject as _translateHeaderObject, translateMediaTypeObject as _translateMediaTypeObject, } from '../content'; const translateMediaTypeObject = (document: any, object: unknown, key: string) => - _translateMediaTypeObject.call(createContext(document), [key, object], 0, []); + _translateMediaTypeObject.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); const translateHeaderObject = (object: unknown, key: string) => - _translateHeaderObject.call(createContext({}), [key, object], 0, []); + _translateHeaderObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); describe('translateMediaTypeObject', () => { afterEach(() => { @@ -23,6 +24,7 @@ describe('translateMediaTypeObject', () => { it('given empty object, should return nothing', () => { expect(translateMediaTypeObject({}, {}, 'mediaType')).toStrictEqual({ + id: expect.any(String), encodings: [], examples: [], mediaType: 'mediaType', @@ -39,6 +41,7 @@ describe('translateMediaTypeObject', () => { 'mediaType', ), ).toStrictEqual({ + id: expect.any(String), encodings: [], examples: [], mediaType: 'mediaType', @@ -56,7 +59,17 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given multiple examples should translate to IHttpContent', () => { @@ -70,7 +83,17 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given encodings should translate each encoding to array item', () => { @@ -109,7 +132,33 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + encodings: [ + { + headers: [ + { + id: expect.any(String), + }, + ], + }, + { + headers: [ + { + id: expect.any(String), + }, + ], + }, + ], + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given complex nested media type object should translate correctly', () => { @@ -143,7 +192,31 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + encodings: [ + { + headers: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + }, + ], + }, + ], + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('should skip nullish headers', () => { @@ -165,7 +238,17 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given encoding with no style it should not throw an error', () => { @@ -257,7 +340,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toHaveProperty('examples', [{ key: 'default', value: defaultExample }]); + ).toHaveProperty('examples', [{ id: expect.any(String), key: 'default', value: defaultExample }]); }); }); @@ -279,8 +362,8 @@ describe('translateMediaTypeObject', () => { 'mediaType', ), ).toHaveProperty('examples', [ - { key: 'default', value: { name: 'root default value' } }, - { key: 'example', value: { name: 'root example value' } }, + { id: expect.any(String), key: 'default', value: { name: 'root default value' } }, + { id: expect.any(String), key: 'example', value: { name: 'root example value' } }, ]); }); }); @@ -377,7 +460,20 @@ describe('translateHeaderObject', () => { }, 'header-name', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('should handle nullish value gracefully', () => { diff --git a/src/oas3/transformers/__tests__/request.test.ts b/src/oas3/transformers/__tests__/request.test.ts index 0be85e70..26e0c3dc 100644 --- a/src/oas3/transformers/__tests__/request.test.ts +++ b/src/oas3/transformers/__tests__/request.test.ts @@ -1,21 +1,11 @@ -import { mockPassthroughImplementation } from '@stoplight/test-utils'; - -import { createContext } from '../../../context'; -import { translateMediaTypeObject } from '../content'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToRequest as _translateToRequest } from '../request'; -jest.mock('../content'); - -const translateToRequest = (path: Record, operation: Record) => { - const ctx = createContext({ paths: { '/api': path } }); - return _translateToRequest.call(ctx, path, operation); -}; +const translateToRequest = (path: Record, operation: Record) => + _translateToRequest.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), path, operation); describe('translateOas3ToRequest', () => { - beforeEach(() => { - mockPassthroughImplementation(translateMediaTypeObject); - }); - it('given no request body should translate parameters', () => { const operation = { parameters: [ @@ -81,6 +71,18 @@ describe('translateOas3ToRequest', () => { post: operation, }; - expect(translateToRequest(path, operation)).toMatchSnapshot(); + expect(translateToRequest(path, operation)).toMatchSnapshot({ + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + }); }); }); diff --git a/src/oas3/transformers/__tests__/responses.test.ts b/src/oas3/transformers/__tests__/responses.test.ts index 9f5a929d..55531a4a 100644 --- a/src/oas3/transformers/__tests__/responses.test.ts +++ b/src/oas3/transformers/__tests__/responses.test.ts @@ -1,11 +1,12 @@ import type { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToResponses as _translateToResponses } from '../responses'; const translateToResponses = (document: DeepPartial, responses: unknown) => - _translateToResponses.call(createContext(document), responses); + _translateToResponses.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), responses); describe('translateToOas3Responses', () => { it('given empty dictionary should return empty array', () => { @@ -13,49 +14,99 @@ describe('translateToOas3Responses', () => { }); it('given a response in dictionary should translate', () => { - expect( - translateToResponses( - {}, - { - default: { - content: { - 'fake-content-type': {}, - }, - description: 'descr', - headers: { - 'fake-header-name-1': { - description: 'calls per hour allowed by the user', - schema: { - type: 'integer', - format: 'int32', - }, - example: 1000, + const responses = translateToResponses( + {}, + { + default: { + content: { + 'fake-content-type': {}, + }, + description: 'descr', + headers: { + 'fake-header-name-1': { + description: 'calls per hour allowed by the user', + schema: { + type: 'integer', + format: 'int32', }, - 'fake-header-name-2': { - description: 'calls per hour allowed by the user', - schema: { - type: 'integer', - format: 'int32', - }, - required: true, - example: 1000, + example: 1000, + }, + 'fake-header-name-2': { + description: 'calls per hour allowed by the user', + schema: { + type: 'integer', + format: 'int32', }, + required: true, + example: 1000, }, }, - 200: { - content: { - 'fake-content-type-200': { - example: 'dumb', - }, + }, + 200: { + content: { + 'fake-content-type-200': { + example: 'dumb', }, - description: 'descr 200', - headers: { - 'fake-header-name-200': {}, + }, + description: 'descr 200', + headers: { + 'fake-header-name-200': {}, + }, + }, + }, + ); + + expect(responses).toHaveLength(2); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), }, + ], + }, + ], + headers: [ + { + id: expect.any(String), + }, + ], + }); + expect(responses[1]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + }, + ], + headers: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), }, }, - ), - ).toMatchSnapshot(); + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given a response with nullish headers in dictionary should translate', () => { @@ -72,6 +123,7 @@ describe('translateToOas3Responses', () => { ), ).toStrictEqual([ { + id: expect.any(String), code: '200', contents: [], headers: [], @@ -92,6 +144,7 @@ describe('translateToOas3Responses', () => { ), ).toStrictEqual([ { + id: expect.any(String), code: '201', contents: [], description: 'description 201', @@ -149,12 +202,15 @@ describe('translateToOas3Responses', () => { expect(translateToResponses(document, document.paths!['/user'].get.responses)).toEqual([ { + id: expect.any(String), code: '200', contents: [ { + id: expect.any(String), encodings: [], examples: [ { + id: expect.any(String), key: 'my-example', value: { id: 1, @@ -163,6 +219,7 @@ describe('translateToOas3Responses', () => { ], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { id: { @@ -208,13 +265,16 @@ describe('translateToOas3Responses', () => { const expected = [ { + id: expect.any(String), code: '200', contents: [], description: 'OK', headers: [ { + id: expect.any(String), name: 'X-Page', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', }, @@ -224,6 +284,7 @@ describe('translateToOas3Responses', () => { encodings: [], examples: [ { + id: expect.any(String), key: '__default', value: 3, }, diff --git a/src/oas3/transformers/__tests__/securities.test.ts b/src/oas3/transformers/__tests__/securities.test.ts index b682c291..f24f7c46 100644 --- a/src/oas3/transformers/__tests__/securities.test.ts +++ b/src/oas3/transformers/__tests__/securities.test.ts @@ -1,12 +1,13 @@ import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { OperationSecurities } from '../../accessors'; import { translateToSecurities as _translateToSecurities } from '../securities'; const translateToSecurities = (document: DeepPartial, operationSecurities: OperationSecurities) => - _translateToSecurities.call(createContext(document), operationSecurities); + _translateToSecurities.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), operationSecurities); describe('securities', () => { describe('translateToSecurities', () => { @@ -29,6 +30,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'basic-security', type: 'http', description: 'a description', @@ -57,6 +59,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'digest-security', type: 'http', description: 'a description', @@ -86,6 +89,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'bearer-security', type: 'http', description: 'a description', @@ -115,6 +119,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'openIdConnect-security', type: 'openIdConnect', description: 'a description', @@ -144,6 +149,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'header', @@ -173,6 +179,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'basic-security', type: 'http', description: 'a description', @@ -207,6 +214,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-flow-security', @@ -240,6 +248,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { password: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'password-flow-security', @@ -273,6 +282,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { clientCredentials: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'clientCredentials-flow-security', @@ -307,6 +317,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { authorizationCode: { @@ -371,6 +382,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), type: 'http', description: 'a description', key: 'http-security', @@ -378,6 +390,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', @@ -386,6 +399,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', @@ -404,17 +418,20 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), type: 'http', description: 'a description', key: 'http-security', }, { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', type: 'oauth2', }, { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', diff --git a/src/oas3/transformers/__tests__/servers.test.ts b/src/oas3/transformers/__tests__/servers.test.ts index d6573d06..543249b0 100644 --- a/src/oas3/transformers/__tests__/servers.test.ts +++ b/src/oas3/transformers/__tests__/servers.test.ts @@ -1,12 +1,18 @@ import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToServers as _translateToServers } from '../servers'; const translateToServers = ( document: DeepPartial & { paths: { '/pet': { get: Record } } }, -) => _translateToServers.call(createContext(document), document.paths['/pet'], document.paths['/pet'].get); +) => + _translateToServers.call( + createContext(document, resolveRef, DEFAULT_ID_GENERATOR), + document.paths['/pet'], + document.paths['/pet'].get, + ); describe('translateToServers', () => { it('translate single ServerObject to IServer', () => { @@ -68,6 +74,7 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { + id: expect.any(String), description: 'description', url: 'http://stoplight.io/path', variables: { @@ -112,6 +119,7 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { + id: expect.any(String), description: 'description', url: 'http://stoplight.io/pet.get', }, @@ -142,6 +150,7 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { + id: expect.any(String), description: 'description', url: 'http://stoplight.io/pet', }, diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index 7b33152f..f96cc07f 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -1,6 +1,5 @@ import { isPlainObject } from '@stoplight/json'; import { - Dictionary, HttpParamStyles, IHttpEncoding, IHttpHeaderParam, @@ -11,12 +10,15 @@ import { import type { JSONSchema7 } from 'json-schema'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { ArrayCallbackParameters, Fragment } from '../../types'; -import { entries, maybeResolveLocalRef } from '../../utils'; +import { entries } from '../../utils'; import { isHeaderObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; +import { translateToExample } from './examples'; const ACCEPTABLE_STYLES: (string | undefined)[] = [ HttpParamStyles.Form, @@ -25,7 +27,7 @@ const ACCEPTABLE_STYLES: (string | undefined)[] = [ HttpParamStyles.DeepObject, ]; -function isAcceptableStyle( +function hasAcceptableStyle( encodingPropertyObject: T, ): encodingPropertyObject is T & { style: @@ -37,12 +39,14 @@ function isAcceptableStyle( return typeof encodingPropertyObject.style === 'string' && ACCEPTABLE_STYLES.includes(encodingPropertyObject.style); } -const translateEncodingPropertyObject: Oas3TranslateFunction< - ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, - Optional -> = function ([property, encodingPropertyObject]) { +const translateEncodingPropertyObject = withContext< + Oas3TranslateFunction< + ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, + Optional + > +>(function ([property, encodingPropertyObject]) { if (!isPlainObject(encodingPropertyObject)) return; - if (!isAcceptableStyle(encodingPropertyObject)) return; + if (!hasAcceptableStyle(encodingPropertyObject)) return; return { property, @@ -64,16 +68,19 @@ const translateEncodingPropertyObject: Oas3TranslateFunction< isString, ), }; -}; +}); -export const translateHeaderObject = < +export const translateHeaderObject = withContext< Oas3TranslateFunction, Optional> ->function ([name, unresolvedHeaderObject]) { +>(function ([name, unresolvedHeaderObject]) { const headerObject = this.maybeResolveLocalRef(unresolvedHeaderObject); if (!isPlainObject(headerObject)) return; + const id = this.generateId(`http_header-${this.parentId}-${name}`); + if (!isHeaderObject(headerObject)) { return { + id, encodings: [], examples: [], name, @@ -86,6 +93,7 @@ export const translateHeaderObject = < const contentValue = isPlainObject(contentObject) ? Object.values(contentObject)[0] : null; const baseContent: IHttpHeaderParam = { + id, name, style: HttpParamStyles.Simple, explode: !!headerObject.explode, @@ -128,14 +136,14 @@ export const translateHeaderObject = < } if ('example' in contentValue) { - examples.push(transformDefaultExample.call(this, '__default_content', contentValue.example)); + examples.push(translateToDefaultExample.call(this, '__default_content', contentValue.example)); } } examples.push(...entries(headerObject.examples).map(translateToExample, this).filter(isNonNullable)); if ('example' in headerObject) { - examples.push(transformDefaultExample.call(this, '__default', headerObject.example)); + examples.push(translateToDefaultExample.call(this, '__default', headerObject.example)); } return { @@ -143,34 +151,33 @@ export const translateHeaderObject = < encodings, examples, }; -}; +}); -const translateSchemaMediaTypeObject: Oas3TranslateFunction<[schema: unknown], Optional> = function ( - schema, -) { - if (!isPlainObject(schema)) return; +const translateSchemaMediaTypeObject = withContext>>( + function (schema) { + if (!isPlainObject(schema)) return; - return translateSchemaObject.call(this, schema); -}; + return translateSchemaObject.call(this, schema); + }, +); -export const translateMediaTypeObject: Oas3TranslateFunction< - ArrayCallbackParameters<[mediaType: string, mediaObject: unknown]>, - Optional -> = function ([mediaType, mediaObject]) { +export const translateMediaTypeObject = withContext< + Oas3TranslateFunction, Optional> +>(function ([mediaType, mediaObject]) { if (!isPlainObject(mediaObject)) return; - const resolvedMediaObject = resolveMediaObject(this.document, mediaObject); - const { schema, encoding, examples } = resolvedMediaObject; - + const id = this.generateId(`http_media-${this.parentId}-${mediaType}`); + const { schema, encoding, examples } = mediaObject; const jsonSchema = translateSchemaMediaTypeObject.call(this, schema); - const example = resolvedMediaObject.example || jsonSchema?.examples?.[0]; + const defaultExample = 'example' in mediaObject ? mediaObject.example : jsonSchema?.examples?.[0]; return { + id, mediaType, // Note that I'm assuming all references are resolved examples: [ - example ? transformDefaultExample.call(this, 'default', example) : undefined, + defaultExample !== undefined ? translateToDefaultExample.call(this, 'default', defaultExample) : undefined, ...entries(examples).map(translateToExample, this), ].filter(isNonNullable), encodings: entries(encoding).map(translateEncodingPropertyObject, this).filter(isNonNullable), @@ -182,51 +189,4 @@ export const translateMediaTypeObject: Oas3TranslateFunction< isNonNullable, ), }; -}; - -function resolveMediaObject(document: unknown, maybeMediaObject: Dictionary) { - const mediaObject = { ...maybeMediaObject }; - if (isPlainObject(mediaObject.schema)) { - mediaObject.schema = maybeResolveLocalRef(document, mediaObject.schema); - } - - if (isPlainObject(mediaObject.examples)) { - const examples = { ...mediaObject.examples }; - mediaObject.examples = examples; - for (const [exampleName, exampleValue] of entries(examples)) { - examples[exampleName] = maybeResolveLocalRef(document, exampleValue); - } - } - - return mediaObject; -} - -const transformDefaultExample: Oas3TranslateFunction<[key: string, value: unknown], INodeExample> = function ( - key, - value, -) { - return { - value, - key, - }; -}; - -const translateToExample: Oas3TranslateFunction< - ArrayCallbackParameters<[key: string, example: unknown]>, - Optional -> = function ([key, example]) { - if (!isPlainObject(example)) return; - - return { - value: example.value, - key, - - ...pickBy( - { - summary: example.summary, - description: example.description, - }, - isString, - ), - }; -}; +}); diff --git a/src/oas3/transformers/examples.ts b/src/oas3/transformers/examples.ts new file mode 100644 index 00000000..5bfb8032 --- /dev/null +++ b/src/oas3/transformers/examples.ts @@ -0,0 +1,33 @@ +import { isPlainObject } from '@stoplight/json'; +import { INodeExample, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); + +import { withContext } from '../../context'; +import { isString } from '../../guards'; +import { getSharedKey } from '../../oas/resolver'; +import type { ArrayCallbackParameters } from '../../types'; +import type { Oas3TranslateFunction } from '../types'; + +export const translateToExample = withContext< + Oas3TranslateFunction, Optional> +>(function ([key, example]) { + const resolvedExample = this.maybeResolveLocalRef(example); + + if (!isPlainObject(resolvedExample)) return; + + const actualKey = this.context === 'service' ? getSharedKey(resolvedExample) : key; + + return { + id: this.generateId(`example-${this.parentId}-${actualKey}`), + value: resolvedExample.value, + key, + + ...pickBy( + { + summary: resolvedExample.summary, + description: resolvedExample.description, + }, + isString, + ), + }; +}); diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index f7997316..ee64fb8a 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -4,140 +4,127 @@ import type { IHttpOperationRequest, IHttpOperationRequestBody, IHttpParam, - INodeExample, - INodeExternalExample, Optional, } from '@stoplight/types'; import { HttpParamStyles } from '@stoplight/types'; import type { JSONSchema7 } from 'json-schema'; import type { ParameterObject } from 'openapi3-ts'; -import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; import { OasVersion } from '../../oas'; -import { getValidOasParameters } from '../../oas/accessors'; +import { createOasParamsIterator } from '../../oas/accessors'; import { isValidParamStyle } from '../../oas/guards'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import type { ArrayCallbackParameters, Fragment } from '../../types'; import { entries } from '../../utils'; import { isRequestBodyObject } from '../guards'; -import type { Oas3TranslateFunction } from '../types'; +import { Oas3TranslateFunction } from '../types'; import { translateMediaTypeObject } from './content'; +import { translateToExample } from './examples'; +import pickBy = require('lodash.pickby'); -export const translateRequestBody: Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> = - function (requestBodyObject) { - const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); - - if (isRequestBodyObject(resolvedRequestBodyObject)) { - return { - contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), - - ...pickBy( - { - required: resolvedRequestBodyObject.required, - description: resolvedRequestBodyObject.description, - }, - isNonNullable, - ), - }; - } - - return { contents: [] }; - }; +export const translateRequestBody = withContext< + Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> +>(function (requestBodyObject) { + const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); + const id = this.generateId(`http_request_body-${this.parentId}`); + + if (isRequestBodyObject(resolvedRequestBodyObject)) { + return { + id, + contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), + ...pickBy( + { + required: resolvedRequestBodyObject.required, + description: resolvedRequestBodyObject.description, + }, + isNonNullable, + ), + }; + } -const translateParameterObjectSchema: Oas3TranslateFunction< - [parameterObject: Fragment], - Optional -> = function (parameterObject) { + return { id, contents: [] }; +}); + +const translateParameterObjectSchema = withContext< + Oas3TranslateFunction<[parameterObject: Record], Optional> +>(function (parameterObject) { if (!isPlainObject(parameterObject.schema)) return; return translateSchemaObject.call(this, { ...parameterObject.schema, ...('example' in parameterObject ? { example: parameterObject.example } : null), }); -}; +}); -const translateToExample: Oas3TranslateFunction< - ArrayCallbackParameters<[key: string, example: unknown]>, - Optional -> = function ([key, example]) { - if (!isPlainObject(example)) return; +export const translateParameterObject = withContext< + Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> +>(function (parameterObject) { + const kind = parameterObject.in === 'path' ? 'path_param' : parameterObject.in; + const name = parameterObject.name; + const id = this.generateId(`http_${kind}-${this.parentId}-${name}`); + const schema = translateParameterObjectSchema.call(this, parameterObject); - if (!('value' in example) && typeof example.externalValue !== 'string') return; + const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); + const hasDefaultExample = examples.some(({ key }) => key.includes('default')); return { - key, - - ...(typeof example.externalValue === 'string' - ? { externalValue: example.externalValue } - : { value: example.value }), + id, + name, + deprecated: !!parameterObject.deprecated, + required: !!parameterObject.required, + style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, + explode: !!(parameterObject.explode ?? parameterObject.style === HttpParamStyles.Form), + examples: [ + !hasDefaultExample && parameterObject.example !== undefined + ? translateToDefaultExample.call(this, 'default', parameterObject.example) + : undefined, + ...examples, + ].filter(isNonNullable), ...pickBy( { - summary: example.summary, - description: example.description, + description: parameterObject.description, }, isString, ), - }; -}; - -export const translateParameterObject: Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> = - function (parameterObject) { - const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); - - const hasDefaultExample = examples.some(({ key }) => key.includes('default')); - const schema = translateParameterObjectSchema.call(this, parameterObject); - return { - name: parameterObject.name, - deprecated: !!parameterObject.deprecated, - style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, - explode: !!(parameterObject.explode ?? parameterObject.style === HttpParamStyles.Form), - examples: - 'example' in parameterObject && !hasDefaultExample - ? [{ key: 'default', value: parameterObject.example }, ...examples] - : examples, - - ...pickBy( - { - description: parameterObject.description, - }, - isString, - ), - - ...pickBy( - { - schema, - content: parameterObject.content, - }, - isPlainObject, - ), - }; + ...pickBy( + { + schema, + content: parameterObject.content, + }, + isPlainObject, + ), + }; +}); + +const iterateOasParams = createOasParamsIterator(OasVersion.OAS3); + +export const translateToRequest = withContext< + Oas3TranslateFunction<[path: Record, operation: Record], IHttpOperationRequest> +>(function (path, operation) { + const params: Omit & { header: IHttpHeaderParam[] } = { + header: [], + query: [], + cookie: [], + path: [], }; -export const translateToRequest: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IHttpOperationRequest> = - function (path, operation) { - const params: Omit & { header: IHttpHeaderParam[] } = { - header: [], - query: [], - cookie: [], - path: [], - }; - - for (const param of getValidOasParameters(this.document, OasVersion.OAS3, operation.parameters, path.parameters)) { - const { in: key } = param; - const target = params[key]; - if (!Array.isArray(target)) continue; + for (const param of iterateOasParams.call(this, path, operation)) { + const { in: key } = param; + const target = params[key]; + if (!Array.isArray(target)) continue; - target.push(translateParameterObject.call(this, param) as any); - } + target.push(translateParameterObject.call(this, param) as any); + } - return { - body: translateRequestBody.call(this, operation?.requestBody), - headers: params.header, - query: params.query, - cookie: params.cookie, - path: params.path, - }; + return { + body: translateRequestBody.call(this, operation?.requestBody), + headers: params.header, + query: params.query, + cookie: params.cookie, + path: params.path, }; +}); diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index 2f7225f9..a569b283 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,21 +1,28 @@ import type { IHttpOperationResponse, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import type { ArrayCallbackParameters } from '../../types'; +import { getSharedKey } from '../../oas/resolver'; +import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; import { isResponseObject } from '../guards'; -import type { Oas3TranslateFunction } from '../types'; +import { Oas3TranslateFunction } from '../types'; import { translateHeaderObject, translateMediaTypeObject } from './content'; -const translateToResponse: Oas3TranslateFunction< - ArrayCallbackParameters<[statusCode: string, response: unknown]>, - Optional -> = function ([statusCode, response]) { +const translateToResponse = withContext< + Oas3TranslateFunction< + ArrayCallbackParameters<[statusCode: string, response: unknown]>, + Optional + > +>(function ([statusCode, response]) { const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; + const actualKey = this.context === 'service' ? getSharedKey(resolvedResponse) : statusCode; + return { + id: this.generateId(`http_response-${this.parentId}-${actualKey}`), code: statusCode, headers: entries(resolvedResponse.headers).map(translateHeaderObject, this).filter(isNonNullable), contents: entries(resolvedResponse.content).map(translateMediaTypeObject, this).filter(isNonNullable), @@ -27,7 +34,7 @@ const translateToResponse: Oas3TranslateFunction< isString, ), }; -}; +}); export const translateToResponses: Oas3TranslateFunction<[responses: unknown], IHttpOperationResponse[]> = function ( responses, diff --git a/src/oas3/transformers/securities.ts b/src/oas3/transformers/securities.ts index 53618e93..cfd11162 100644 --- a/src/oas3/transformers/securities.ts +++ b/src/oas3/transformers/securities.ts @@ -1,27 +1,34 @@ import { isPlainObject } from '@stoplight/json'; import type { IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; +import { HttpSecurityScheme } from '@stoplight/types'; import type { SecuritySchemeObject } from 'openapi3-ts'; +import { withContext } from '../../context'; import { isNonNullable } from '../../guards'; import { ArrayCallbackParameters } from '../../types'; -import { getSecurities, SecurityWithKey } from '../accessors'; +import { getSecurities } from '../accessors'; import { isOAuthFlowObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = +export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], HttpSecurityScheme[][]> = function (operationSecurities) { const securities = getSecurities(this.document, operationSecurities); return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); }; -export const translateToSingleSecurity: Oas3TranslateFunction< - [ArrayCallbackParameters & { type: 'mutualTLS' })>[0]], - Optional -> = function (securityScheme) { - const { key } = securityScheme; - - const baseObject: { key: string; description?: string } = { +export const translateToSingleSecurity = withContext< + Oas3TranslateFunction< + [ + ArrayCallbackParameters< + [key: string, security: SecuritySchemeObject | (Omit & { type: 'mutualTLS' })] + >[0], + ], + Optional + > +>(function ([key, securityScheme]) { + const baseObject: { id: string; key: string; description?: string } = { + id: this.generateId(`http_security-${this.ids.service}-${key}`), key, }; @@ -79,7 +86,7 @@ export const translateToSingleSecurity: Oas3TranslateFunction< } return undefined; -}; +}); function transformFlows(flows: Optional): IOauthFlowObjects { const transformedFlows: IOauthFlowObjects = {}; diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index 9a22e861..6e9f5265 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,38 +1,41 @@ import type { INodeVariable, IServer, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import { ArrayCallbackParameters, Fragment } from '../../types'; +import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; import { isServerObject, isServerVariableObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -export const translateToServers: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IServer[]> = function ( - path, - operation, -) { +export const translateToServers = withContext< + Oas3TranslateFunction<[path: Record, operation: Record], IServer[]> +>(function (path, operation) { let servers; if (Array.isArray(operation.servers)) { servers = operation.servers; } else if (Array.isArray(path.servers)) { servers = path.servers; + this.context = 'path'; } else if (Array.isArray(this.document.servers)) { servers = this.document.servers; + this.context = 'service'; } else { return []; } return servers.map(translateToServer, this).filter(isNonNullable); -}; +}); -export const translateToServer: Oas3TranslateFunction, Optional> = function ( - server, -) { +export const translateToServer = withContext< + Oas3TranslateFunction, Optional> +>(function (server) { if (!isServerObject(server)) return; const variables = translateServerVariables.call(this, server.variables); return { + id: this.generateId(`http_server-${this.parentId}-${server.url}`), url: server.url, ...pickBy( @@ -50,7 +53,7 @@ export const translateToServer: Oas3TranslateFunction { }), ).toEqual( expect.objectContaining({ - security: [[{ key: 'http-0', scheme: 'basic', type: 'http' }]], + security: [ + [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'http-0', scheme: 'basic', type: 'http' }], + ], }), ); }); @@ -123,6 +125,7 @@ describe('transformPostmanCollectionOperation()', () => { request: expect.objectContaining({ headers: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), description: 'Hawk Authorization Header', name: 'Authorization', required: true, @@ -166,7 +169,15 @@ describe('transformPostmanCollectionOperation()', () => { ).toEqual( expect.objectContaining({ request: expect.objectContaining({ - query: [{ description: 'OAuth2 Access Token', name: 'access_token', required: true, style: 'form' }], + query: [ + { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + description: 'OAuth2 Access Token', + name: 'access_token', + required: true, + style: 'form', + }, + ], }), }), ); @@ -223,7 +234,7 @@ describe('transformPostmanCollectionOperation()', () => { }), ).toEqual( expect.objectContaining({ - servers: [{ url: 'https://example.com:666' }], + servers: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), url: 'https://example.com:666' }], }), ); }); @@ -260,9 +271,20 @@ describe('transformPostmanCollectionOperation()', () => { ).toEqual( expect.objectContaining({ request: expect.objectContaining({ - body: { contents: [{ examples: [{ key: 'default', value: 'testTESTtest' }], mediaType: 'text/plain' }] }, + body: { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + contents: [ + { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'testTESTtest' }, + ], + mediaType: 'text/plain', + }, + ], + }, }), - servers: [{ url: 'https://example.com' }], + servers: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), url: 'https://example.com' }], }), ); }); @@ -331,8 +353,10 @@ describe('transformPostmanCollectionOperations()', () => { request: expect.objectContaining({ headers: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'a header', }, @@ -345,8 +369,10 @@ describe('transformPostmanCollectionOperations()', () => { required: false, }, { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'b header', }, diff --git a/src/postman/__tests__/service.test.ts b/src/postman/__tests__/service.test.ts index 2f67d4f2..6d111dbf 100644 --- a/src/postman/__tests__/service.test.ts +++ b/src/postman/__tests__/service.test.ts @@ -30,6 +30,7 @@ describe('transformPostmanCollectionService()', () => { id: expect.any(String), securitySchemes: [ { + id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', diff --git a/src/postman/__tests__/util.test.ts b/src/postman/__tests__/util.test.ts index 43408104..2b5ae3c6 100644 --- a/src/postman/__tests__/util.test.ts +++ b/src/postman/__tests__/util.test.ts @@ -6,7 +6,7 @@ describe('transformStringValueToSchema()', () => { it('returns param with schema and example', () => { expect(transformStringValueToSchema('test')).toEqual({ schema: { type: 'string' }, - examples: [{ key: 'default', value: 'test' }], + examples: [{ id: expect.any(String), key: 'default', value: 'test' }], }); }); }); diff --git a/src/postman/id.ts b/src/postman/id.ts new file mode 100644 index 00000000..e39b995f --- /dev/null +++ b/src/postman/id.ts @@ -0,0 +1,8 @@ +export function generateId() { + return ( + '_gen_' + + Math.round(Math.pow(8, 6) * Math.random()) + .toString(16) + .padStart(6, '0') + ); +} diff --git a/src/postman/operation.ts b/src/postman/operation.ts index 57ac6653..e70106d6 100644 --- a/src/postman/operation.ts +++ b/src/postman/operation.ts @@ -1,5 +1,5 @@ -import { HttpSecurityScheme, IHttpOperation } from '@stoplight/types'; -import { Collection, CollectionDefinition, Item, RequestAuth, Url } from 'postman-collection'; +import type { HttpSecurityScheme, IHttpOperation } from '@stoplight/types'; +import type { Collection, CollectionDefinition, Item, RequestAuth, Url } from 'postman-collection'; import { mergeOperations, mergeResponses } from '../merge'; import { transformRequest } from './transformers/request'; @@ -11,7 +11,7 @@ import { transformSecuritySchemes, } from './transformers/securityScheme'; import { transformServer } from './transformers/server'; -import { PostmanCollectionHttpOperationTransformer } from './types'; +import type { PostmanCollectionHttpOperationTransformer } from './types'; import { resolveCollection, transformDescriptionDefinition } from './util'; export const transformPostmanCollectionOperations = (document: CollectionDefinition): IHttpOperation[] => { diff --git a/src/postman/service.ts b/src/postman/service.ts index b2e3888a..4a781b3d 100644 --- a/src/postman/service.ts +++ b/src/postman/service.ts @@ -1,6 +1,6 @@ -import { CollectionDefinition } from 'postman-collection'; +import type { CollectionDefinition } from 'postman-collection'; -import { HttpServiceTransformer } from '../types'; +import type { HttpServiceTransformer } from '../types'; import { isStandardSecurityScheme, transformSecuritySchemes } from './transformers/securityScheme'; import { resolveCollection, resolveVersion, transformDescriptionDefinition } from './util'; diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index fd51a78c..006de934 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -7,8 +7,10 @@ describe('transformQueryParam()', () => { describe('value is set', () => { it('transforms correctly', () => { expect(transformQueryParam(new QueryParam({ key: 'testKey', value: 'testValue' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'testValue', }, @@ -26,6 +28,7 @@ describe('transformQueryParam()', () => { describe('value is null', () => { it('transforms correctly', () => { expect(transformQueryParam(new QueryParam({ key: 'testKey', value: null }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testKey', style: 'form', required: true, @@ -36,6 +39,7 @@ describe('transformQueryParam()', () => { describe('key is null', () => { it('transforms correctly with key being empty string', () => { expect(transformQueryParam(new QueryParam({ key: null, value: null }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: '', style: 'form', required: true, @@ -48,6 +52,7 @@ describe('transformHeader()', () => { describe('value is defined', () => { it('result contains schema', () => { expect(transformHeader(new Header({ key: 'testKey', value: 'some string' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testkey', schema: { type: 'string', @@ -56,6 +61,7 @@ describe('transformHeader()', () => { required: true, examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'some string', }, @@ -67,6 +73,7 @@ describe('transformHeader()', () => { describe('value is not defined', () => { it('results does not contain schema', () => { expect(transformHeader(new Header({ key: 'testKey' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testkey', style: 'simple', required: true, @@ -78,8 +85,8 @@ describe('transformHeader()', () => { describe('transformPathParams()', () => { it('transforms correctly', () => { expect(transformPathParams(['elem1', ':param1', ':param2', 'elem2'])).toEqual([ - { name: 'param1', style: 'simple', required: true }, - { name: 'param2', style: 'simple', required: true }, + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'param1', style: 'simple', required: true }, + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'param2', style: 'simple', required: true }, ]); }); }); @@ -91,9 +98,11 @@ describe('transformBody()', () => { describe('body is correctly defined json', () => { it('returns body containing example, schema and media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '{"a":"b"}' }), 'application/nice+json')).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { a: 'b' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { a: 'b' } }], mediaType: 'application/nice+json', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -116,9 +125,11 @@ describe('transformBody()', () => { describe('body is not a correct JSON', () => { it('returns body containing example and media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '"a":"b"' }), 'application/json')).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: '"a":"b"' }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: '"a":"b"' }], mediaType: 'application/json', }, ], @@ -131,9 +142,11 @@ describe('transformBody()', () => { describe('media type is defined', () => { it('returns body containing example and given media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '' }), 'application/xml')).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: '' }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: '' }], mediaType: 'application/xml', }, ], @@ -144,9 +157,13 @@ describe('transformBody()', () => { describe('media type is not defined', () => { it('returns body containing example and text/plain media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: "I'm a goat. Bleeet!" }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: "I'm a goat. Bleeet!" }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: "I'm a goat. Bleeet!" }, + ], mediaType: 'text/plain', }, ], @@ -179,9 +196,13 @@ describe('transformBody()', () => { 'multipart/test+form-data', ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1', k2: 'v2' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1', k2: 'v2' } }, + ], mediaType: 'multipart/test+form-data', schema: { type: 'object', @@ -206,9 +227,11 @@ describe('transformBody()', () => { }), ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1' } }], mediaType: 'multipart/form-data', schema: { type: 'object', @@ -234,9 +257,13 @@ describe('transformBody()', () => { ); expect(result).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: expect.any(Object) }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: expect.any(Object) }, + ], mediaType: 'multipart/test+form-data', schema: { type: 'object', @@ -272,9 +299,11 @@ describe('transformBody()', () => { describe('body is not defined', () => { it('returns default empty body', () => { expect(transformBody(new RequestBody({ mode: 'formdata' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: {} }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: {} }], mediaType: 'multipart/form-data', schema: { properties: {}, type: 'object' }, }, @@ -300,9 +329,13 @@ describe('transformBody()', () => { 'application/test+x-www-form-urlencoded', ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1', k2: 'v2' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1', k2: 'v2' } }, + ], mediaType: 'application/test+x-www-form-urlencoded', schema: { type: 'object', @@ -327,9 +360,11 @@ describe('transformBody()', () => { }), ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1' } }], mediaType: 'application/x-www-form-urlencoded', schema: { type: 'object', @@ -347,9 +382,11 @@ describe('transformBody()', () => { describe('body is not defined', () => { it('returns default empty body', () => { expect(transformBody(new RequestBody({ mode: 'urlencoded' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: {} }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: {} }], mediaType: 'application/x-www-form-urlencoded', schema: { properties: {}, type: 'object' }, }, diff --git a/src/postman/transformers/__tests__/request.test.ts b/src/postman/transformers/__tests__/request.test.ts index 679d0da3..27b782b9 100644 --- a/src/postman/transformers/__tests__/request.test.ts +++ b/src/postman/transformers/__tests__/request.test.ts @@ -15,26 +15,30 @@ describe('transformRequest()', () => { ), ).toEqual({ body: { + id: expect.any(String), contents: [ { - examples: [{ key: 'default', value: 'test' }], + id: expect.any(String), + examples: [{ id: expect.any(String), key: 'default', value: 'test' }], mediaType: 'text/plain', }, ], }, headers: [ { + id: expect.any(String), name: 'header', schema: { type: 'string' }, - examples: [{ key: 'default', value: 'a header' }], + examples: [{ id: expect.any(String), key: 'default', value: 'a header' }], style: 'simple', required: true, }, ], - path: [{ name: 'param', style: 'simple', required: true }], + path: [{ id: expect.any(String), name: 'param', style: 'simple', required: true }], query: [ { - examples: [{ key: 'default', value: 'b' }], + id: expect.any(String), + examples: [{ id: expect.any(String), key: 'default', value: 'b' }], schema: { type: 'string' }, name: 'a', style: 'form', diff --git a/src/postman/transformers/__tests__/response.test.ts b/src/postman/transformers/__tests__/response.test.ts index 93cb7ea4..dc502b9a 100644 --- a/src/postman/transformers/__tests__/response.test.ts +++ b/src/postman/transformers/__tests__/response.test.ts @@ -23,11 +23,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), code: '200', contents: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { "I'm a JSON": 'Jieeeet!', @@ -51,8 +54,10 @@ describe('transformResponse()', () => { ], headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'application/json', }, @@ -65,8 +70,10 @@ describe('transformResponse()', () => { style: 'simple', }, { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Domain=.example.com; Path=/', }, @@ -90,6 +97,7 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [], }); @@ -108,6 +116,7 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', description: 'Test', headers: [], @@ -138,11 +147,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=300; Domain=example.com; Path=/; Secure; HttpOnly', @@ -174,11 +186,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Domain=example.com; Path=/', }, @@ -207,11 +222,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: '=; Domain=example.com; Path=/', }, @@ -246,11 +264,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Domain=example.com; Path=/; a=p; u=d', }, @@ -282,11 +303,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Expires=Fri, 11 Aug 2017 09:04:08 GMT; Domain=example.com; Path=/', }, diff --git a/src/postman/transformers/__tests__/securitySchemes.spec.ts b/src/postman/transformers/__tests__/securitySchemes.spec.ts index 13e0828a..396c4fa4 100644 --- a/src/postman/transformers/__tests__/securitySchemes.spec.ts +++ b/src/postman/transformers/__tests__/securitySchemes.spec.ts @@ -33,6 +33,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'basic', @@ -70,6 +71,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'digest', @@ -97,6 +99,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'bearer', @@ -135,6 +138,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-apiKey', type: 'apiKey', name: 'TestApiKey', @@ -168,6 +172,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-apiKey', type: 'apiKey', name: 'TestApiKey', @@ -208,12 +213,14 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { + id: expect.any(String), key: 'default', value: 'OAuth realm="karol@stoplight.io",oauth_consumer_key="a_consumer_key",oauth_token="a_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="123123123123",oauth_nonce="a",oauth_version="1.0",oauth_signature="a_signature"', @@ -241,12 +248,14 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { + id: expect.any(String), key: 'default', value: 'OAuth realm="a_realm",oauth_consumer_key="a_consumer_key",oauth_token="a_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="0",oauth_nonce="a",oauth_version="1.0",oauth_signature="a_signature"', @@ -274,29 +283,32 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, - { name: 'oauth_token', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: false, - examples: [{ key: 'default', value: 'HMAC-SHA1' }], + examples: [{ id: expect.any(String), key: 'default', value: 'HMAC-SHA1' }], }, { + id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: false, schema: { type: 'string' }, - examples: [{ key: 'default', value: '123123123123' }], + examples: [{ id: expect.any(String), key: 'default', value: '123123123123' }], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: false, - examples: [{ key: 'default', value: '1.0' }], + examples: [{ id: expect.any(String), key: 'default', value: '1.0' }], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, ], }); }); @@ -317,29 +329,32 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, - { name: 'oauth_token', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: false, examples: [], }, { + id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: false, schema: { type: 'string' }, examples: [], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: false, examples: [], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, ], }); }); @@ -359,29 +374,32 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: true }, - { name: 'oauth_token', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: true }, { + id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: true, - examples: [{ key: 'default', value: 'HMAC-SHA1' }], + examples: [{ id: expect.any(String), key: 'default', value: 'HMAC-SHA1' }], }, { + id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: true, schema: { type: 'string' }, - examples: [{ key: 'default', value: '123123123123' }], + examples: [{ id: expect.any(String), key: 'default', value: '123123123123' }], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: true }, { + id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: true, - examples: [{ key: 'default', value: '1.0' }], + examples: [{ id: expect.any(String), key: 'default', value: '1.0' }], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: true }, ], }); }); @@ -414,6 +432,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-oauth2', description: 'OAuth2 Access Token', scheme: 'bearer', @@ -448,6 +467,7 @@ describe('transformSecurityScheme()', () => { type: 'queryParams', queryParams: [ { + id: expect.any(String), name: 'access_token', style: HttpParamStyles.Form, description: 'OAuth2 Access Token', @@ -483,6 +503,7 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'Hawk Authorization Header', @@ -517,16 +538,19 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'X-Amz-Security-Token', style: HttpParamStyles.Simple, required: true, }, { + id: expect.any(String), name: 'X-Amz-Date', style: HttpParamStyles.Simple, required: true, }, { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -559,6 +583,7 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'Akamai EdgeGrid Authorization Header', @@ -588,6 +613,7 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'NTLM Authorization Header', @@ -647,6 +673,7 @@ describe('transformPostmanSecuritySchemes()', () => { ).toEqual([ { securityScheme: { + id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', @@ -655,6 +682,7 @@ describe('transformPostmanSecuritySchemes()', () => { }, { securityScheme: { + id: expect.any(String), key: 'http-1', scheme: 'digest', type: 'http', @@ -753,6 +781,7 @@ describe('transformPostmanSecuritySchemes()', () => { ).toEqual([ { securityScheme: { + id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', @@ -767,26 +796,26 @@ describe('transformPostmanSecuritySchemes()', () => { describe.each<[string, PostmanSecurityScheme, PostmanSecurityScheme, boolean]>([ [ 'two equal security schemes', - { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, - { type: 'securityScheme', securityScheme: { key: '2', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '2', type: 'http', scheme: 'basic' } }, true, ], [ 'different types', - { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, { type: 'headerParams', headerParams: [] }, false, ], [ 'two different', - { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, - { type: 'securityScheme', securityScheme: { key: '2', type: 'http', scheme: 'digest' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '2', type: 'http', scheme: 'digest' } }, false, ], [ 'two equal query params', - { type: 'queryParams', queryParams: [{ name: 'token', style: HttpParamStyles.Form }] }, - { type: 'queryParams', queryParams: [{ name: 'token', style: HttpParamStyles.Form }] }, + { type: 'queryParams', queryParams: [{ id: '', name: 'token', style: HttpParamStyles.Form }] }, + { type: 'queryParams', queryParams: [{ id: '', name: 'token', style: HttpParamStyles.Form }] }, true, ], ])('given %s security schemes', (desc, scheme1, scheme2, result) => { diff --git a/src/postman/transformers/__tests__/server.test.ts b/src/postman/transformers/__tests__/server.test.ts index 4410a412..4f623f42 100644 --- a/src/postman/transformers/__tests__/server.test.ts +++ b/src/postman/transformers/__tests__/server.test.ts @@ -6,13 +6,19 @@ describe('transformServer()', () => { describe('host is defined', () => { describe('port is defined', () => { it('produces server with port', () => { - expect(transformServer(new Url('https://example.com:666/path'))).toEqual({ url: 'https://example.com:666' }); + expect(transformServer(new Url('https://example.com:666/path'))).toEqual({ + id: expect.any(String), + url: 'https://example.com:666', + }); }); }); describe('port is not defined', () => { it('produces server without port', () => { - expect(transformServer(new Url('https://example.com/path'))).toEqual({ url: 'https://example.com' }); + expect(transformServer(new Url('https://example.com/path'))).toEqual({ + id: expect.any(String), + url: 'https://example.com', + }); }); }); }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 341e8fa4..96370a5c 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -9,14 +9,16 @@ import { import type { JSONSchema7 } from 'json-schema'; // @ts-ignore import * as jsonSchemaGenerator from 'json-schema-generator'; -import { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; +import type { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; import { convertSchema } from '../../oas/transformers/schema'; +import { generateId } from '../id'; import { transformDescriptionDefinition, transformStringValueToSchema } from '../util'; export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { return { + id: generateId(), name: queryParam.key || '', style: HttpParamStyles.Form, required: true, @@ -26,6 +28,7 @@ export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { export function transformHeader(header: Header): IHttpHeaderParam { return { + id: generateId(), name: header.key.toLowerCase(), style: HttpParamStyles.Simple, required: true, @@ -37,6 +40,7 @@ export function transformPathParams(segments: string[]): IHttpPathParam[] { return segments.reduce((params, segment) => { if (segment.startsWith(':')) { params.push({ + id: generateId(), name: segment.substring(1), style: HttpParamStyles.Simple, required: true, @@ -51,17 +55,19 @@ export function transformBody(body: RequestBody, mediaType?: string): IHttpOpera switch (body.mode) { case 'raw': if (!body.raw) return; - return { contents: [transformRawBody(body.raw, mediaType)] }; + return { id: generateId(), contents: [transformRawBody(body.raw, mediaType)] }; case 'formdata': if (!body.formdata) return; return { + id: generateId(), contents: [transformParamsBody(body.formdata, mediaType || 'multipart/form-data')], }; case 'urlencoded': if (!body.urlencoded) return; return { + id: generateId(), contents: [transformParamsBody(body.urlencoded, mediaType || 'application/x-www-form-urlencoded')], }; } @@ -75,9 +81,11 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): const parsed = JSON.parse(raw); return { + id: generateId(), mediaType, examples: [ { + id: generateId(), key: 'default', value: parsed, }, @@ -90,9 +98,11 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): } return { + id: generateId(), mediaType, examples: [ { + id: generateId(), key: 'default', value: raw, }, @@ -116,6 +126,7 @@ function transformParamsBody( }); return { + id: generateId(), mediaType, schema: { type: 'object', @@ -126,6 +137,7 @@ function transformParamsBody( }, examples: [ { + id: generateId(), key: 'default', value: paramsList.reduce((values, param) => { values[param.name] = param.value; @@ -135,12 +147,3 @@ function transformParamsBody( ], }; } - -function generateId() { - return ( - '_gen_' + - Math.round(Math.pow(8, 6) * Math.random()) - .toString(16) - .padStart(6, '0') - ); -} diff --git a/src/postman/transformers/response.ts b/src/postman/transformers/response.ts index 97f8b350..a2efe60c 100644 --- a/src/postman/transformers/response.ts +++ b/src/postman/transformers/response.ts @@ -1,6 +1,7 @@ import { HttpParamStyles, IHttpHeaderParam, IHttpOperationResponse } from '@stoplight/types'; import { Cookie, Response } from 'postman-collection'; +import { generateId } from '../id'; import { transformDescriptionDefinition } from '../util'; import { transformHeader, transformRawBody } from './params'; @@ -9,6 +10,7 @@ export function transformResponse(response: Response): IHttpOperationResponse { const mediaType = response.headers.get('content-type'); return { + id: generateId(), code: String(response.code), description: response.description && transformDescriptionDefinition(response.description), headers: headers.concat(response.cookies.map(transformCookie).filter((c: Cookie) => c)), @@ -29,9 +31,11 @@ function transformCookie(cookie: Cookie): IHttpHeaderParam | undefined { if (cookie.extensions) params.push(...cookie.extensions.map(({ key, value }) => `${key}=${value}`)); return { + id: generateId(), name: 'set-cookie', examples: [ { + id: generateId(), key: 'default', value: params.join('; '), }, diff --git a/src/postman/transformers/securityScheme.ts b/src/postman/transformers/securityScheme.ts index 816b2cb3..ba48f408 100644 --- a/src/postman/transformers/securityScheme.ts +++ b/src/postman/transformers/securityScheme.ts @@ -1,6 +1,8 @@ import { HttpParamStyles, HttpSecurityScheme, IHttpHeaderParam, IHttpQueryParam } from '@stoplight/types'; -import { Collection, RequestAuth } from 'postman-collection'; -import isEqual = require('lodash.isequal'); +import type { Collection, RequestAuth } from 'postman-collection'; + +import { isEqual } from '../../utils'; +import { generateId } from '../id'; export type PostmanSecurityScheme = StandardSecurityScheme | QuerySecurityScheme | HeaderSecurityScheme; @@ -36,12 +38,14 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { + id: generateId(), key: 'default', value: 'OAuth ' + @@ -69,31 +73,38 @@ export function transformSecurityScheme( return { type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required }, - { name: 'oauth_token', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_token', style: HttpParamStyles.Form, required }, { + id: generateId(), name: 'oauth_signature_method', style: HttpParamStyles.Form, required, examples: parameters.has('signatureMethod') - ? [{ key: 'default', value: parameters.get('signatureMethod') }] + ? [{ id: generateId(), key: 'default', value: parameters.get('signatureMethod') }] : [], }, { + id: generateId(), name: 'oauth_timestamp', style: HttpParamStyles.Form, required, schema: { type: 'string' }, - examples: parameters.has('timestamp') ? [{ key: 'default', value: parameters.get('timestamp') }] : [], + examples: parameters.has('timestamp') + ? [{ id: generateId(), key: 'default', value: parameters.get('timestamp') }] + : [], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_nonce', style: HttpParamStyles.Form, required }, { + id: generateId(), name: 'oauth_version', style: HttpParamStyles.Form, required, - examples: parameters.has('version') ? [{ key: 'default', value: parameters.get('version') }] : [], + examples: parameters.has('version') + ? [{ id: generateId(), key: 'default', value: parameters.get('version') }] + : [], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_signature', style: HttpParamStyles.Form, required }, ], }; } @@ -103,6 +114,7 @@ export function transformSecurityScheme( type: 'queryParams', queryParams: [ { + id: generateId(), name: 'access_token', description: 'OAuth2 Access Token', style: HttpParamStyles.Form, @@ -114,6 +126,7 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { + id: generateId(), key: nextKey('oauth2'), type: 'http', scheme: 'bearer', @@ -126,6 +139,7 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { + id: generateId(), key: nextKey('apiKey'), type: 'apiKey', name: parameters.get('key'), @@ -139,6 +153,7 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { + id: generateId(), key: nextKey('http'), type: 'http', scheme: auth.type, @@ -150,6 +165,7 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', description: 'Hawk Authorization Header', required: true, @@ -167,16 +183,19 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'X-Amz-Security-Token', style: HttpParamStyles.Simple, required: true, }, { + id: generateId(), name: 'X-Amz-Date', style: HttpParamStyles.Simple, required: true, }, { + id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -190,6 +209,7 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -203,6 +223,7 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', description: 'NTLM Authorization Header', required: true, @@ -227,9 +248,7 @@ export function isPostmanSecuritySchemeEqual(pss1: PostmanSecurityScheme, pss2: if (pss1.type !== pss2.type) return false; if (isStandardSecurityScheme(pss1) && isStandardSecurityScheme(pss2)) { - const { key: _key, ...pss1SecurityScheme } = pss1.securityScheme; - const { key: _key2, ...pss2SecurityScheme } = pss2.securityScheme; - return isEqual(pss1SecurityScheme, pss2SecurityScheme); + return isEqual({ ...pss1.securityScheme, key: '' }, { ...pss2.securityScheme, key: '' }); } return isEqual(pss1, pss2); diff --git a/src/postman/transformers/server.ts b/src/postman/transformers/server.ts index e2898163..ccda6fd3 100644 --- a/src/postman/transformers/server.ts +++ b/src/postman/transformers/server.ts @@ -1,10 +1,12 @@ -import { IServer } from '@stoplight/types'; -import { Url } from 'postman-collection'; +import type { IServer } from '@stoplight/types'; +import type { Url } from 'postman-collection'; + +import { generateId } from '../id'; export function transformServer(url: Url): IServer | undefined { try { const origin = new URL(url.toString()).origin; - return origin ? { url: origin } : undefined; + return origin ? { id: generateId(), url: origin } : undefined; } catch { return undefined; } diff --git a/src/postman/types.ts b/src/postman/types.ts index 0503aeff..350c7555 100644 --- a/src/postman/types.ts +++ b/src/postman/types.ts @@ -1,6 +1,6 @@ -import { CollectionDefinition } from 'postman-collection'; +import type { CollectionDefinition } from 'postman-collection'; -import { HttpOperationTransformer } from '../types'; +import type { HttpOperationTransformer } from '../types'; export type PostmanCollectionHttpOperationTransformer = HttpOperationTransformer<{ document: CollectionDefinition; diff --git a/src/postman/util.ts b/src/postman/util.ts index 8c8e321a..ca5277f6 100644 --- a/src/postman/util.ts +++ b/src/postman/util.ts @@ -1,10 +1,13 @@ -import { IHttpParam } from '@stoplight/types'; +import type { IHttpParam } from '@stoplight/types'; import { Collection, CollectionDefinition, DescriptionDefinition, Version } from 'postman-collection'; +import { generateId } from './id'; + export function transformStringValueToSchema(value: string): Pick { return { examples: [ { + id: generateId(), key: 'default', value, }, diff --git a/src/track.ts b/src/track.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/types.ts b/src/types.ts index 5ec95eff..ae632ad8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,13 @@ import type { IHttpOperation, IHttpService } from '@stoplight/types'; export type Fragment = Record; +export type IdGenerator = (value: string) => string; + +export type RefResolver = ( + this: TransformerContext, + input: Fragment & { $ref: string }, +) => unknown; + export interface ITransformServiceOpts { document: T; } @@ -16,14 +23,20 @@ export interface ITransformOperationOpts { export type HttpOperationTransformer = (opts: T) => IHttpOperation; +export type ArrayCallbackParameters = [T, number, T[]]; + +export type AvailableContext = 'service' | 'path' | 'operation'; + export type TransformerContext = { - maybeResolveLocalRef(target: unknown): unknown; document: T; + context: AvailableContext; + parentId: string; + readonly ids: Record; + generateId(template: string): string; + maybeResolveLocalRef(target: unknown): unknown; }; export type TranslateFunction = ( this: TransformerContext, ...params: P ) => R; - -export type ArrayCallbackParameters = [T, number, T[]]; diff --git a/src/utils.ts b/src/utils.ts index 3d177946..39c3dee5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,21 +1,15 @@ -import { hasRef, isLocalRef, isPlainObject, resolveInlineRef } from '@stoplight/json'; - -export const maybeResolveLocalRef = (document: unknown, target: unknown): unknown => { - if (isPlainObject(document) && hasRef(target) && isLocalRef(target.$ref)) { - try { - return resolveInlineRef(document, target.$ref); - } catch { - return target; - } - } - - return target; -}; - -export { isPlainObject as isDictionary }; +import { isPlainObject } from '@stoplight/json'; +import isEqualWith = require('lodash.isequalwith'); export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; export function entries(o: T): [string, T][]; export function entries(o: T): [string, T][] { return isPlainObject(o) ? Object.entries(o as T) : []; } + +export function isEqual(left: unknown, right: unknown) { + return isEqualWith(left, right, (value, other, indexOrKey) => { + if (indexOrKey === 'id') return true; + return; + }); +} diff --git a/yarn.lock b/yarn.lock index 88d5e7d9..beb5000d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,14 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" + integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== + dependencies: + "@babel/highlight" "^7.10.1" + +"@babel/code-frame@^7.10.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== @@ -68,7 +75,12 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.10.1", "@babel/helper-validator-identifier@^7.16.7": +"@babel/helper-validator-identifier@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" + integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== + +"@babel/helper-validator-identifier@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== @@ -82,16 +94,25 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" +"@babel/highlight@^7.10.1": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" + integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== + dependencies: + "@babel/helper-validator-identifier" "^7.10.1" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" - integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": +"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== @@ -166,7 +187,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/template@^7.3.3", "@babel/template@^7.8.3", "@babel/template@^7.8.6": +"@babel/runtime@^7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" + integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== @@ -175,7 +203,7 @@ "@babel/parser" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== @@ -1082,17 +1110,17 @@ integrity sha512-Cj3waLFR9bYLG8yvgkjXMxOfiVdewRyrKdH5RQpttVNfKj5UF+mElofPcHn/UfiOuxK3t5rbsdMfS26LK5tdQg== "@stoplight/types@^12.3.0": - version "12.4.0" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.4.0.tgz#7a90e747d3f531742316b7b2969e6c45efd3e968" - integrity sha512-ELf0dOdROaKEE0iw1YF9qbL9QjIVfdyiCoBPpUQmvQej9F7tQR/TsYMvyUb8kz+rd49u3hGP/sirVuaTKI1ZOg== + version "12.5.0" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.5.0.tgz#ebbeeb8c874de30e4cd9a1a2a6c8d6062c155da0" + integrity sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg== dependencies: "@types/json-schema" "^7.0.4" utility-types "^3.10.0" -"@stoplight/types@^13.0.0-beta.4": - version "13.0.0-beta.4" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.4.tgz#5ea5e93957b20a5effedcbd101e6e9cde7118e48" - integrity sha512-q9Dbyfi1DcZjTuDwptVLTCbeQgtpfTQAKQkbcrs64ayfvc7dPwO+81vYr0dSJsXzlFQktZv5M+eq0QPtJy/HGg== +"@stoplight/types@^13.0.0-beta.5": + version "13.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.5.tgz#31804f3d1e6369a6daeff985dd0ca7ab27637f70" + integrity sha512-qZgWB3QxoOJL2TsbSfPSyStsnH7BwvAljcT2xa0ZowP3/Eh16OYS/PWzQzTU/GZ+btyqpowGgTragLUgipQAjQ== dependencies: "@types/json-schema" "^7.0.4" utility-types "^3.10.0" @@ -1102,7 +1130,7 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" integrity sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": +"@types/babel__core@^7.0.0": version "7.1.9" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== @@ -1113,6 +1141,17 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" +"@types/babel__core@^7.1.7": + version "7.1.8" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.8.tgz#057f725aca3641f49fc11c7a87a9de5ec588a5d7" + integrity sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__generator@*": version "7.6.0" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.0.tgz#f1ec1c104d1bb463556ecb724018ab788d0c172a" @@ -1185,12 +1224,12 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/json-schema@7.0.5": +"@types/json-schema@7.0.5", "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== -"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.9": version "7.0.10" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== @@ -1200,10 +1239,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash.isequal@^4.5.5": - version "4.5.5" - resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz#4fed1b1b00bef79e305de0352d797e9bb816c8ff" - integrity sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg== +"@types/lodash.isequalwith@^4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/lodash.isequalwith/-/lodash.isequalwith-4.4.6.tgz#8c5dee2b2fdc05cfa79a3a77cd40cc8909ef79c9" + integrity sha512-55fjBOrhse+SLkqGlvUq1yQ/sDvsi93+ngLIG22DPEoeL4c/d1rtWMMK55pP4nKOjkSWv8ks1q0HaXXCgPr81w== dependencies: "@types/lodash" "*" @@ -1226,6 +1265,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== +"@types/memoizee@^0.4.7": + version "0.4.7" + resolved "https://registry.yarnpkg.com/@types/memoizee/-/memoizee-0.4.7.tgz#fa0025b5ea1c8acb85fd52a76982c5a096c2c91e" + integrity sha512-EMtvWnRC/W0KYmXxbPJAMg/M1OXkFboSpd/IzkNMDXfoFPE4uom1RHFl8q7m/4R0y9eG1yaoaIPiMHk0vMA0eA== + "@types/node@*", "@types/node@>= 8": version "13.9.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.1.tgz#96f606f8cd67fb018847d9b61e93997dabdefc72" @@ -1295,13 +1339,13 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.15.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" - integrity sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw== + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz#c28ef7f2e688066db0b6a9d95fb74185c114fb9a" + integrity sha512-u6Db5JfF0Esn3tiAKELvoU5TpXVSkOpZ78cEGn/wXtT2RVqs2vkt4ge6N8cRCyw7YVKhmmLDbwI2pg92mlv7cA== dependencies: - "@typescript-eslint/scope-manager" "5.16.0" - "@typescript-eslint/type-utils" "5.16.0" - "@typescript-eslint/utils" "5.16.0" + "@typescript-eslint/scope-manager" "5.15.0" + "@typescript-eslint/type-utils" "5.15.0" + "@typescript-eslint/utils" "5.15.0" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -1310,68 +1354,68 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.15.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.16.0.tgz#e4de1bde4b4dad5b6124d3da227347616ed55508" - integrity sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA== + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.15.0.tgz#95f603f8fe6eca7952a99bfeef9b85992972e728" + integrity sha512-NGAYP/+RDM2sVfmKiKOCgJYPstAO40vPAgACoWPO/+yoYKSgAXIFaBKsV8P0Cc7fwKgvj27SjRNX4L7f4/jCKQ== dependencies: - "@typescript-eslint/scope-manager" "5.16.0" - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/typescript-estree" "5.16.0" + "@typescript-eslint/scope-manager" "5.15.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/typescript-estree" "5.15.0" debug "^4.3.2" -"@typescript-eslint/scope-manager@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz#7e7909d64bd0c4d8aef629cdc764b9d3e1d3a69a" - integrity sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ== +"@typescript-eslint/scope-manager@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz#d97afab5e0abf4018d1289bd711be21676cdd0ee" + integrity sha512-EFiZcSKrHh4kWk0pZaa+YNJosvKE50EnmN4IfgjkA3bTHElPtYcd2U37QQkNTqwMCS7LXeDeZzEqnsOH8chjSg== dependencies: - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/visitor-keys" "5.16.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/visitor-keys" "5.15.0" -"@typescript-eslint/type-utils@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz#b482bdde1d7d7c0c7080f7f2f67ea9580b9e0692" - integrity sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ== +"@typescript-eslint/type-utils@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz#d2c02eb2bdf54d0a645ba3a173ceda78346cf248" + integrity sha512-KGeDoEQ7gHieLydujGEFLyLofipe9PIzfvA/41urz4hv+xVxPEbmMQonKSynZ0Ks2xDhJQ4VYjB3DnRiywvKDA== dependencies: - "@typescript-eslint/utils" "5.16.0" + "@typescript-eslint/utils" "5.15.0" debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/types@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.16.0.tgz#5827b011982950ed350f075eaecb7f47d3c643ee" - integrity sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g== +"@typescript-eslint/types@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.15.0.tgz#c7bdd103843b1abae97b5518219d3e2a0d79a501" + integrity sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA== -"@typescript-eslint/typescript-estree@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz#32259459ec62f5feddca66adc695342f30101f61" - integrity sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ== +"@typescript-eslint/typescript-estree@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz#81513a742a9c657587ad1ddbca88e76c6efb0aac" + integrity sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA== dependencies: - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/visitor-keys" "5.16.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/visitor-keys" "5.15.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/utils@5.16.0", "@typescript-eslint/utils@^5.10.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.16.0.tgz#42218b459d6d66418a4eb199a382bdc261650679" - integrity sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ== +"@typescript-eslint/utils@5.15.0", "@typescript-eslint/utils@^5.10.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.15.0.tgz#468510a0974d3ced8342f37e6c662778c277f136" + integrity sha512-081rWu2IPKOgTOhHUk/QfxuFog8m4wxW43sXNOMSCdh578tGJ1PAaWPsj42LOa7pguh173tNlMigsbrHvh/mtA== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.16.0" - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/typescript-estree" "5.16.0" + "@typescript-eslint/scope-manager" "5.15.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/typescript-estree" "5.15.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz#f27dc3b943e6317264c7492e390c6844cd4efbbb" - integrity sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g== +"@typescript-eslint/visitor-keys@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz#5669739fbf516df060f978be6a6dce75855a8027" + integrity sha512-+vX5FKtgvyHbmIJdxMJ2jKm9z2BIlXJiuewI8dsDYMp5LzPUcuTT78Ya5iwvQg3VqSVdmxyM8Anj1Jeq7733ZQ== dependencies: - "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/types" "5.15.0" eslint-visitor-keys "^3.0.0" JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: @@ -1411,9 +1455,9 @@ acorn-walk@^7.1.1: integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + version "7.3.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" + integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" @@ -1456,7 +1500,17 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.5.5: +ajv@^6.10.0: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1466,6 +1520,16 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.5.5: + version "6.12.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" + integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" @@ -1500,7 +1564,12 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -2732,12 +2801,12 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== dependencies: - ms "2.1.2" + ms "^2.1.1" debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -2746,13 +2815,27 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.1.0, debug@^3.2.7: +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" +debug@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -2808,7 +2891,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.3: +define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -3043,7 +3126,24 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -3158,9 +3258,9 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.12.0" eslint-plugin-jest@^26.1.1: - version "26.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.2.tgz#0f1a15c62889fffc3f78a773749d672f1bedb15f" - integrity sha512-1bXCoRODPkGN06n9KAMls4Jm0eyS+0Q/LWcIxhqWR2ycV0Z7lnx2c10idk4dtFIJY5xStgiIr5snC6/rxcXpbw== + version "26.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.1.tgz#7176dd745ef8bca3070263f62cdf112f2dfc9aa1" + integrity sha512-HRKOuPi5ADhza4ZBK5ufyNXy28bXXkib87w+pQqdvBhSTsamndh6sIAKPAUl8y0/n9jSWBdTPslrwtKWqkp8dA== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -3313,7 +3413,12 @@ estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + +estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -3505,6 +3610,18 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== +fast-glob@^3.1.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" + integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -3683,6 +3800,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +fnv-plus@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/fnv-plus/-/fnv-plus-1.3.1.tgz#c34cb4572565434acb08ba257e4044ce2b006d67" + integrity sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -3774,7 +3896,12 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2, fsevents@~2.3.2: +fsevents@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3921,6 +4048,13 @@ git-raw-commits@^2.0.0: split2 "^2.0.0" through2 "^3.0.0" +glob-parent@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3998,7 +4132,19 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@^11.0.0, globby@^11.0.4: +globby@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154" + integrity sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + +globby@^11.0.4: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -4089,7 +4235,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-symbols@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -4296,6 +4447,11 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" +ignore@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" + integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== + ignore@^5.1.8, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -4309,7 +4465,15 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: +import-fresh@^3.0.0, import-fresh@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -4508,7 +4672,12 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.4: +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== + +is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== @@ -4622,7 +4791,14 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4731,6 +4907,13 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" +is-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" + integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw== + dependencies: + has-symbols "^1.0.1" + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -4764,14 +4947,26 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.5, is-string@^1.0.7: +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" -is-symbol@^1.0.2, is-symbol@^1.0.3: +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== @@ -4870,7 +5065,20 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: +istanbul-lib-instrument@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" + integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== + dependencies: + "@babel/core" "^7.7.5" + "@babel/parser" "^7.7.5" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== @@ -5269,7 +5477,15 @@ jest-watcher@^26.1.0: jest-util "^26.1.0" string-length "^4.0.1" -jest-worker@^26.1.0, jest-worker@^26.2.1: +jest-worker@^26.1.0: + version "26.1.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.1.0.tgz#65d5641af74e08ccd561c240e7db61284f82f33d" + integrity sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ== + dependencies: + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -5826,10 +6042,10 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.isequalwith@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0" + integrity sha1-Jmcm3dUo+FTyH06pigZWBuD7xrA= lodash.ismatch@^4.4.0: version "4.4.0" @@ -6020,7 +6236,14 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^3.0.0, make-dir@^3.0.2: +make-dir@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" + integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + dependencies: + semver "^6.0.0" + +make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -6163,7 +6386,12 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== + +merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -6173,13 +6401,13 @@ merge@^1.2.1: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== -micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== +micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== dependencies: braces "^3.0.1" - picomatch "^2.2.3" + picomatch "^2.0.5" micromatch@^3.0.4, micromatch@^3.1.4: version "3.1.10" @@ -6200,10 +6428,23 @@ micromatch@^3.0.4, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-db@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== mime-format@2.0.1: version "2.0.1" @@ -6212,12 +6453,19 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.34, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== +mime-types@2.1.32: + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: - mime-db "1.51.0" + mime-db "1.49.0" + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" mime@^2.4.3: version "2.4.4" @@ -6234,7 +6482,14 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4, minimatch@^3.1.2: +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -6254,10 +6509,10 @@ minimist@1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minimist@~0.0.1: version "0.0.10" @@ -6774,7 +7029,12 @@ object-inspect@^1.11.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-keys@^1.0.12, object-keys@^1.1.1: +object-inspect@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -6786,6 +7046,16 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" @@ -7009,13 +7279,20 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" +p-limit@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -7205,7 +7482,12 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -7234,7 +7516,12 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + +picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -7284,9 +7571,9 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postman-collection@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.1.tgz#aba50dfca3c3ce6881b9598958c6a52eec3959d9" - integrity sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.0.tgz#c5833aa3cb82df79cc5d16e5d7399c71a84ea4fa" + integrity sha512-J9IpCMXpGDLN7MGhdMcUbZ0SIWLCcTVdrjTgKVYubkW1sn1KcDqJgsdTr/ItkO8dOXKLuhvnq2QnE5Vrzb3WMA== dependencies: faker "5.5.3" file-type "3.9.0" @@ -7295,7 +7582,7 @@ postman-collection@^4.1.0: liquid-json "0.3.1" lodash "4.17.21" mime-format "2.0.1" - mime-types "2.1.34" + mime-types "2.1.32" postman-url-encoder "3.0.5" semver "7.3.5" uuid "8.3.2" @@ -7690,6 +7977,11 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -7698,7 +7990,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.1: +regexp.prototype.flags@^1.3.1: version "1.4.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== @@ -7856,7 +8148,14 @@ resolve@1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +resolve@^1.19.0, resolve@^1.20.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -8102,13 +8401,18 @@ semver@6.3.0, semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@7.3.5, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" +semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -8303,7 +8607,15 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@^0.5.17, source-map-support@^0.5.6: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -8530,19 +8842,27 @@ string-width@^4.1.0, string-width@^4.2.0: strip-ansi "^6.0.0" string.prototype.matchall@^4.0.6: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== + version "4.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" + integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.19.1" get-intrinsic "^1.1.1" - has-symbols "^1.0.3" + has-symbols "^1.0.2" internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" + regexp.prototype.flags "^1.3.1" side-channel "^1.0.4" +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -8551,6 +8871,14 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -8613,7 +8941,14 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -8650,7 +8985,12 @@ strip-json-comments@3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== + +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -8947,13 +9287,13 @@ ts-node@^8.10.2: yn "3.1.1" tsconfig-paths@^3.12.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.14.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz#4fcc48f9ccea8826c41b9ca093479de7f5018976" + integrity sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.6" + minimist "^1.2.0" strip-bom "^3.0.0" tslib@2.1.0: @@ -9529,11 +9869,18 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0, yaml@^1.7.2: +yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^1.7.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.2.tgz#a29c03f578faafd57dcb27055f9a5d569cb0c3d9" + integrity sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg== + dependencies: + "@babel/runtime" "^7.8.7" + yargs-parser@18.x, yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" From cc2cddcd62638a1b471f390e01e026f3f3c92f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Tue, 29 Mar 2022 00:36:21 +0200 Subject: [PATCH 07/16] Revert "feat: stable ids" This reverts commit 6c96a068efd2e2dc7b381e4fb5f25e6702cddc6e. --- jest.config.js | 1 - package.json | 8 +- src/__tests__/merge.test.ts | 244 +++---- src/__tests__/utils.test.ts | 37 + src/context.ts | 66 +- src/merge.ts | 5 +- .../__snapshots__/operation.test.ts.snap | 323 +-------- .../__snapshots__/service.test.ts.snap | 20 +- src/oas/__tests__/accessors.test.ts | 182 ++--- .../__tests__/fixtures/oas2-kitchen-sink.json | 1 - .../__tests__/fixtures/oas3-kitchen-sink.json | 1 - src/oas/__tests__/service.test.ts | 2 +- src/oas/__tests__/tags.test.ts | 13 +- src/oas/accessors.ts | 68 +- src/oas/operation.ts | 55 +- src/oas/resolveInlineRef.ts | 57 -- src/oas/resolver.ts | 32 - src/oas/service.ts | 5 +- src/oas/tags.ts | 23 +- src/oas/transformers/examples.ts | 18 - .../schema/__tests__/schema.spec.ts | 16 +- src/oas/transformers/schema/index.ts | 42 +- .../__snapshots__/operation.test.ts.snap | 11 +- src/oas2/__tests__/guards.test.ts | 39 ++ src/oas2/__tests__/operation.test.ts | 45 +- src/oas2/__tests__/service.test.ts | 27 +- src/oas2/accessors.ts | 8 +- src/oas2/guards.ts | 8 - src/oas2/index.ts | 1 + src/oas2/operation.ts | 51 +- src/oas2/service.ts | 5 +- .../__snapshots__/params.test.ts.snap | 18 - .../__snapshots__/responses.test.ts.snap | 228 +++---- .../transformers/__tests__/params.test.ts | 75 +-- .../transformers/__tests__/request.test.ts | 5 +- .../transformers/__tests__/responses.test.ts | 146 +--- .../transformers/__tests__/securities.test.ts | 19 +- .../transformers/__tests__/servers.test.ts | 15 +- src/oas2/transformers/params.ts | 135 ++-- src/oas2/transformers/request.ts | 38 +- src/oas2/transformers/responses.ts | 51 +- src/oas2/transformers/securities.ts | 87 +-- src/oas2/transformers/servers.ts | 46 +- src/oas3/__fixtures__/id.json | 214 ------ src/oas3/__fixtures__/output.ts | 489 -------------- .../__snapshots__/operation.test.ts.snap | 36 +- src/oas3/__tests__/accessors.test.ts | 96 ++- src/oas3/__tests__/ids.test.ts | 12 - src/oas3/__tests__/operation.test.ts | 193 +----- src/oas3/__tests__/service.test.ts | 39 +- src/oas3/accessors.ts | 46 +- src/oas3/guards.ts | 5 + src/oas3/index.ts | 1 + src/oas3/operation.ts | 54 +- src/oas3/service.ts | 30 +- .../__snapshots__/content.test.ts.snap | 23 - .../__snapshots__/request.test.ts.snap | 20 +- .../__snapshots__/responses.test.ts.snap | 165 +++-- .../__snapshots__/servers.test.ts.snap | 1 - .../transformers/__tests__/content.test.ts | 120 +--- .../transformers/__tests__/request.test.ts | 32 +- .../transformers/__tests__/responses.test.ts | 135 +--- .../transformers/__tests__/securities.test.ts | 21 +- .../transformers/__tests__/servers.test.ts | 13 +- src/oas3/transformers/content.ts | 114 +++- src/oas3/transformers/examples.ts | 33 - src/oas3/transformers/request.ts | 187 +++--- src/oas3/transformers/responses.ts | 21 +- src/oas3/transformers/securities.ts | 27 +- src/oas3/transformers/servers.ts | 23 +- src/postman/__tests__/operation.test.ts | 36 +- src/postman/__tests__/service.test.ts | 1 - src/postman/__tests__/util.test.ts | 2 +- src/postman/id.ts | 8 - src/postman/operation.ts | 6 +- src/postman/service.ts | 4 +- .../transformers/__tests__/params.test.ts | 63 +- .../transformers/__tests__/request.test.ts | 12 +- .../transformers/__tests__/response.test.ts | 24 - .../__tests__/securitySchemes.spec.ts | 79 +-- .../transformers/__tests__/server.test.ts | 10 +- src/postman/transformers/params.ts | 25 +- src/postman/transformers/response.ts | 4 - src/postman/transformers/securityScheme.ts | 43 +- src/postman/transformers/server.ts | 8 +- src/postman/types.ts | 4 +- src/postman/util.ts | 5 +- src/track.ts | 0 src/types.ts | 19 +- src/utils.ts | 24 +- yarn.lock | 633 ++++-------------- 91 files changed, 1482 insertions(+), 3955 deletions(-) create mode 100644 src/__tests__/utils.test.ts delete mode 100644 src/oas/resolveInlineRef.ts delete mode 100644 src/oas/resolver.ts delete mode 100644 src/oas/transformers/examples.ts create mode 100644 src/oas2/__tests__/guards.test.ts delete mode 100644 src/oas3/__fixtures__/id.json delete mode 100644 src/oas3/__fixtures__/output.ts delete mode 100644 src/oas3/__tests__/ids.test.ts delete mode 100644 src/oas3/transformers/examples.ts delete mode 100644 src/postman/id.ts delete mode 100644 src/track.ts diff --git a/jest.config.js b/jest.config.js index a9426838..4a5b465e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,4 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - testMatch: ['/src/**/__tests__/*.(spec|test).(ts|js)'], }; diff --git a/package.json b/package.json index 89c0da7c..6d059d7e 100644 --- a/package.json +++ b/package.json @@ -53,13 +53,12 @@ }, "dependencies": { "@stoplight/json": "^3.17.2", - "@stoplight/types": "^13.0.0-beta.5", + "@stoplight/types": "^13.0.0-beta.4", "@types/json-schema": "7.0.5", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", - "fnv-plus": "^1.3.1", "json-schema-generator": "^2.0.6", - "lodash.isequalwith": "^4.4.0", + "lodash.isequal": "^4.5.0", "lodash.pick": "^4.4.0", "lodash.pickby": "^4.6.0", "openapi3-ts": "^2.0.1", @@ -72,10 +71,9 @@ "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", - "@types/lodash.isequalwith": "^4.4.6", + "@types/lodash.isequal": "^4.5.5", "@types/lodash.pick": "^4.4.6", "@types/lodash.pickby": "^4.6.6", - "@types/memoizee": "^0.4.7", "@types/postman-collection": "^3.5.3", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", diff --git a/src/__tests__/merge.test.ts b/src/__tests__/merge.test.ts index 031823ca..2c21375c 100644 --- a/src/__tests__/merge.test.ts +++ b/src/__tests__/merge.test.ts @@ -8,66 +8,59 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', headers: [ - { id: 'b', name: '200h1', style: HttpParamStyles.Simple, required: true }, - { id: 'c', name: '200h2', style: HttpParamStyles.Simple, required: true }, + { name: '200h1', style: HttpParamStyles.Simple, required: true }, + { name: '200h2', style: HttpParamStyles.Simple, required: true }, ], }, { - id: 'd', code: '400', headers: [ - { id: 'e', name: '400h1', style: HttpParamStyles.Simple, required: true }, - { id: 'f', name: '400h2', style: HttpParamStyles.Simple, required: true }, + { name: '400h1', style: HttpParamStyles.Simple, required: true }, + { name: '400h2', style: HttpParamStyles.Simple, required: true }, ], }, ], [ { - id: 'g', code: '200', headers: [ - { id: 'h', name: '200h2', style: HttpParamStyles.Simple, required: true }, - { id: 'i', name: '200h3', style: HttpParamStyles.Simple, required: true }, + { name: '200h2', style: HttpParamStyles.Simple, required: true }, + { name: '200h3', style: HttpParamStyles.Simple, required: true }, ], }, { - id: 'j', code: '500', headers: [ - { id: 'k', name: '500h1', style: HttpParamStyles.Simple, required: true }, - { id: 'l', name: '500h2', style: HttpParamStyles.Simple, required: true }, + { name: '500h1', style: HttpParamStyles.Simple, required: true }, + { name: '500h2', style: HttpParamStyles.Simple, required: true }, ], }, ], ), ).toEqual([ { - id: 'a', code: '200', headers: [ - { id: 'b', name: '200h1', style: HttpParamStyles.Simple, required: false }, - { id: 'c', name: '200h2', style: HttpParamStyles.Simple, required: true }, - { id: 'i', name: '200h3', style: HttpParamStyles.Simple, required: false }, + { name: '200h1', style: HttpParamStyles.Simple, required: false }, + { name: '200h2', style: HttpParamStyles.Simple, required: true }, + { name: '200h3', style: HttpParamStyles.Simple, required: false }, ], contents: [], }, { - id: 'd', code: '400', headers: [ - { id: 'e', name: '400h1', style: HttpParamStyles.Simple, required: true }, - { id: 'f', name: '400h2', style: HttpParamStyles.Simple, required: true }, + { name: '400h1', style: HttpParamStyles.Simple, required: true }, + { name: '400h2', style: HttpParamStyles.Simple, required: true }, ], }, { - id: 'j', code: '500', headers: [ - { id: 'k', name: '500h1', style: HttpParamStyles.Simple, required: true }, - { id: 'l', name: '500h2', style: HttpParamStyles.Simple, required: true }, + { name: '500h1', style: HttpParamStyles.Simple, required: true }, + { name: '500h2', style: HttpParamStyles.Simple, required: true }, ], }, ]); @@ -78,50 +71,45 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', headers: [ - { id: 'b', name: 'h1', style: HttpParamStyles.Simple, required: true }, - { id: 'c', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, - { id: 'd', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { name: 'h1', style: HttpParamStyles.Simple, required: true }, + { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, ], [ { - id: 'e', code: '200', headers: [ - { id: 'f', name: 'h1', style: HttpParamStyles.Simple, required: true }, - { id: 'g', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'string' } }, - { id: 'h', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { name: 'h1', style: HttpParamStyles.Simple, required: true }, + { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'string' } }, + { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, { - id: 'i', code: '200', headers: [ - { id: 'j', name: 'h1', style: HttpParamStyles.Simple, required: true }, - { id: 'k', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'boolean' } }, - { id: 'l', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { name: 'h1', style: HttpParamStyles.Simple, required: true }, + { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'boolean' } }, + { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, ], ), ).toEqual([ { - id: 'a', code: '200', headers: [ - { id: 'b', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { name: 'h1', style: HttpParamStyles.Simple, required: true }, { - id: 'c', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { anyOf: [{ type: 'number' }, { type: 'string' }, { type: 'boolean' }] }, }, - { id: 'd', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], contents: [], }, @@ -133,24 +121,21 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', - headers: [{ id: 'b', name: 'h', style: HttpParamStyles.Simple, required: true }], + headers: [{ name: 'h', style: HttpParamStyles.Simple, required: true }], }, ], [ { - id: 'c', code: '200', - headers: [{ id: 'd', name: 'h', style: HttpParamStyles.Simple, required: false }], + headers: [{ name: 'h', style: HttpParamStyles.Simple, required: false }], }, ], ), ).toEqual([ { - id: 'a', code: '200', - headers: [{ id: 'b', name: 'h', style: HttpParamStyles.Simple, required: false }], + headers: [{ name: 'h', style: HttpParamStyles.Simple, required: false }], contents: [], }, ]); @@ -161,24 +146,21 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', - headers: [{ id: 'b', name: 'Oo', style: HttpParamStyles.Simple, required: true }], + headers: [{ name: 'Oo', style: HttpParamStyles.Simple, required: true }], }, ], [ { - id: 'c', code: '200', - headers: [{ id: 'd', name: 'oO', style: HttpParamStyles.Simple, required: true }], + headers: [{ name: 'oO', style: HttpParamStyles.Simple, required: true }], }, ], ), ).toEqual([ { - id: 'a', code: '200', - headers: [{ id: 'b', name: 'Oo', style: HttpParamStyles.Simple, required: true }], + headers: [{ name: 'Oo', style: HttpParamStyles.Simple, required: true }], contents: [], }, ]); @@ -189,67 +171,38 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', - contents: [ - { id: 'a1', mediaType: '200-tion/a-son' }, - { id: 'a2', mediaType: '200-tion/b-son' }, - ], + contents: [{ mediaType: '200-tion/a-son' }, { mediaType: '200-tion/b-son' }], }, { - id: 'b', code: '400', - contents: [ - { id: 'b1', mediaType: '400-tion/a-son' }, - { id: 'b2', mediaType: '400-tion/b-son' }, - ], + contents: [{ mediaType: '400-tion/a-son' }, { mediaType: '400-tion/b-son' }], }, ], [ { - id: 'c', code: '200', - contents: [ - { id: 'c1', mediaType: '200-tion/b-son' }, - { id: 'c2', mediaType: '200-tion/c-son' }, - ], + contents: [{ mediaType: '200-tion/b-son' }, { mediaType: '200-tion/c-son' }], }, { - id: 'd', code: '500', - contents: [ - { id: 'd1', mediaType: '500-tion/a-son' }, - { id: 'd2', mediaType: '500-tion/b-son' }, - ], + contents: [{ mediaType: '500-tion/a-son' }, { mediaType: '500-tion/b-son' }], }, ], ), ).toEqual([ { - id: 'a', code: '200', - contents: [ - { id: 'a1', mediaType: '200-tion/a-son' }, - { id: 'a2', mediaType: '200-tion/b-son' }, - { id: 'c2', mediaType: '200-tion/c-son' }, - ], + contents: [{ mediaType: '200-tion/a-son' }, { mediaType: '200-tion/b-son' }, { mediaType: '200-tion/c-son' }], headers: [], }, { - id: 'b', code: '400', - contents: [ - { id: 'b1', mediaType: '400-tion/a-son' }, - { id: 'b2', mediaType: '400-tion/b-son' }, - ], + contents: [{ mediaType: '400-tion/a-son' }, { mediaType: '400-tion/b-son' }], }, { - id: 'd', code: '500', - contents: [ - { id: 'd1', mediaType: '500-tion/a-son' }, - { id: 'd2', mediaType: '500-tion/b-son' }, - ], + contents: [{ mediaType: '500-tion/a-son' }, { mediaType: '500-tion/b-son' }], }, ]); }); @@ -259,24 +212,17 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', contents: [ - { - id: 'a1', - mediaType: 'application/json', - schema: { type: 'object', properties: { a: { type: 'string' } } }, - }, + { mediaType: 'application/json', schema: { type: 'object', properties: { a: { type: 'string' } } } }, ], }, ], [ { - id: 'b', code: '200', contents: [ { - id: 'b1', mediaType: 'application/json', schema: { type: 'object', properties: { a: { type: 'string' }, b: { type: 'string' } } }, }, @@ -286,11 +232,9 @@ describe('mergeResponses()', () => { ), ).toEqual([ { - id: 'a', code: '200', contents: [ { - id: 'a1', mediaType: 'application/json', schema: { anyOf: [ @@ -310,24 +254,21 @@ describe('mergeResponses()', () => { mergeResponses( [ { - id: 'a', code: '200', - contents: [{ id: 'a1', mediaType: 'Aa/Oo' }], + contents: [{ mediaType: 'Aa/Oo' }], }, ], [ { - id: 'b', code: '200', - contents: [{ id: 'b1', mediaType: 'aA/oO' }], + contents: [{ mediaType: 'aA/oO' }], }, ], ), ).toEqual([ { - id: 'a', code: '200', - contents: [{ id: 'a1', mediaType: 'Aa/Oo' }], + contents: [{ mediaType: 'Aa/Oo' }], headers: [], }, ]); @@ -339,28 +280,12 @@ describe('mergeOperations()', () => { expect( mergeOperations( [ - { - id: '1', - method: 'get', - path: '/a', - responses: [ - { id: 'a', code: '200' }, - { id: 'b', code: '400' }, - ], - }, - { id: '2', method: 'get', path: '/b', responses: [{ id: 'c', code: '200' }] }, + { id: '1', method: 'get', path: '/a', responses: [{ code: '200' }, { code: '400' }] }, + { id: '2', method: 'get', path: '/b', responses: [{ code: '200' }] }, ], [ - { - id: '3', - method: 'get', - path: '/a', - responses: [ - { id: 'd', code: '200' }, - { id: 'e', code: '500' }, - ], - }, - { id: '4', method: 'get', path: '/c', responses: [{ id: 'f', code: '200' }] }, + { id: '3', method: 'get', path: '/a', responses: [{ code: '200' }, { code: '500' }] }, + { id: '4', method: 'get', path: '/c', responses: [{ code: '200' }] }, ], ), ).toEqual([ @@ -368,16 +293,12 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [ - { id: 'a', code: '200', contents: [], headers: [] }, - { id: 'b', code: '400' }, - { id: 'e', code: '500' }, - ], + responses: [{ code: '200', contents: [], headers: [] }, { code: '400' }, { code: '500' }], servers: [], request: {}, }, - { id: '2', method: 'get', path: '/b', responses: [{ id: 'c', code: '200' }] }, - { id: '4', method: 'get', path: '/c', responses: [{ id: 'f', code: '200' }] }, + { id: '2', method: 'get', path: '/b', responses: [{ code: '200' }] }, + { id: '4', method: 'get', path: '/c', responses: [{ code: '200' }] }, ]); }); @@ -389,8 +310,8 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ id: 'a', code: '200' }], - servers: [{ id: 'b', url: 'http://example.com' }], + responses: [{ code: '200' }], + servers: [{ url: 'http://example.com' }], }, ], [ @@ -398,8 +319,8 @@ describe('mergeOperations()', () => { id: '2', method: 'get', path: '/a', - responses: [{ id: 'c', code: '200' }], - servers: [{ id: 'd', url: 'https://example.com' }], + responses: [{ code: '200' }], + servers: [{ url: 'https://example.com' }], }, ], ), @@ -408,11 +329,8 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ id: 'a', code: '200', headers: [], contents: [] }], - servers: [ - { id: 'b', url: 'http://example.com' }, - { id: 'd', url: 'https://example.com' }, - ], + responses: [{ code: '200', headers: [], contents: [] }], + servers: [{ url: 'http://example.com' }, { url: 'https://example.com' }], request: {}, }, ]); @@ -426,24 +344,22 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ id: 'a', code: '200' }], + responses: [{ code: '200' }], request: { headers: [ - { id: 'b', name: 'a', style: HttpParamStyles.Simple, required: true }, - { id: 'c', name: 'b', style: HttpParamStyles.Simple, required: true }, + { name: 'a', style: HttpParamStyles.Simple, required: true }, + { name: 'b', style: HttpParamStyles.Simple, required: true }, ], query: [ - { id: 'd', name: 'a', style: HttpParamStyles.Form, required: true }, - { id: 'e', name: 'b', style: HttpParamStyles.Form, required: true }, + { name: 'a', style: HttpParamStyles.Form, required: true }, + { name: 'b', style: HttpParamStyles.Form, required: true }, ], - path: [{ id: 'f', name: 'a', style: HttpParamStyles.Simple, required: true }], + path: [{ name: 'a', style: HttpParamStyles.Simple, required: true }], body: { - id: 'g', description: 'The cadillac stood by the house', required: true, contents: [ { - id: 'g1', mediaType: 'application/json', schema: { type: 'string' }, }, @@ -457,27 +373,25 @@ describe('mergeOperations()', () => { id: '2', method: 'get', path: '/a', - responses: [{ id: '11', code: '200' }], + responses: [{ code: '200' }], request: { headers: [ - { id: 'b1', name: 'b', style: HttpParamStyles.Simple, required: true }, - { id: 'c1', name: 'c', style: HttpParamStyles.Simple, required: true }, + { name: 'b', style: HttpParamStyles.Simple, required: true }, + { name: 'c', style: HttpParamStyles.Simple, required: true }, ], query: [ - { id: 'd1', name: 'b', style: HttpParamStyles.Form, required: true }, - { id: 'e1', name: 'c', style: HttpParamStyles.Form, required: true }, + { name: 'b', style: HttpParamStyles.Form, required: true }, + { name: 'c', style: HttpParamStyles.Form, required: true }, ], path: [ - { id: 'f1', name: 'a', style: HttpParamStyles.Simple, required: true }, - { id: 'g1', name: 'b', style: HttpParamStyles.Simple, required: true }, + { name: 'a', style: HttpParamStyles.Simple, required: true }, + { name: 'b', style: HttpParamStyles.Simple, required: true }, ], body: { - id: 'h1', description: 'And the yanks they were within', required: true, contents: [ { - id: 'h2', mediaType: 'application/json', schema: { type: 'number' }, }, @@ -492,29 +406,27 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ id: 'a', code: '200', headers: [], contents: [] }], + responses: [{ code: '200', headers: [], contents: [] }], request: { headers: [ - { id: 'b', name: 'a', style: HttpParamStyles.Simple, required: false }, - { id: 'c', name: 'b', style: HttpParamStyles.Simple, required: true }, - { id: 'c1', name: 'c', style: HttpParamStyles.Simple, required: false }, + { name: 'a', style: HttpParamStyles.Simple, required: false }, + { name: 'b', style: HttpParamStyles.Simple, required: true }, + { name: 'c', style: HttpParamStyles.Simple, required: false }, ], query: [ - { id: 'd', name: 'a', style: HttpParamStyles.Form, required: false }, - { id: 'e', name: 'b', style: HttpParamStyles.Form, required: true }, - { id: 'e1', name: 'c', style: HttpParamStyles.Form, required: false }, + { name: 'a', style: HttpParamStyles.Form, required: false }, + { name: 'b', style: HttpParamStyles.Form, required: true }, + { name: 'c', style: HttpParamStyles.Form, required: false }, ], path: [ - { id: 'f', name: 'a', style: HttpParamStyles.Simple, required: true }, - { id: 'g1', name: 'b', style: HttpParamStyles.Simple, required: false }, + { name: 'a', style: HttpParamStyles.Simple, required: true }, + { name: 'b', style: HttpParamStyles.Simple, required: false }, ], body: { - id: 'g', required: true, description: 'The cadillac stood by the house; And the yanks they were within', contents: [ { - id: 'g1', mediaType: 'application/json', schema: { anyOf: [{ type: 'string' }, { type: 'number' }] }, }, diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts new file mode 100644 index 00000000..57cb1795 --- /dev/null +++ b/src/__tests__/utils.test.ts @@ -0,0 +1,37 @@ +import { maybeResolveLocalRef } from '../utils'; + +describe('maybeResolveLocalRef()', () => { + it('follows $refs', () => { + expect( + maybeResolveLocalRef( + { + a: { + $ref: '#/b', + }, + b: { + c: 'woo', + }, + }, + { $ref: '#/a/c' }, + ), + ).toEqual('woo'); + }); + + it('handles invalid $refs', () => { + expect(maybeResolveLocalRef({ a: true }, { $ref: '#a' })).toStrictEqual({ $ref: '#a' }); + }); + + it('handles circular references', () => { + const document = { + get a(): unknown { + return this.a; + }, + }; + + expect( + maybeResolveLocalRef(document, { + $ref: '#/a', + }), + ).toStrictEqual({ $ref: '#/a' }); + }); +}); diff --git a/src/context.ts b/src/context.ts index 0a4c3334..d13668e0 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,67 +1,9 @@ -import { hasRef, isLocalRef } from '@stoplight/json'; -// @ts-ignore -import * as fnv from 'fnv-plus'; +import { TransformerContext } from './types'; +import { maybeResolveLocalRef } from './utils'; -import type { - AvailableContext, - Fragment, - IdGenerator, - RefResolver, - TransformerContext, - TranslateFunction, -} from './types'; - -export const DEFAULT_ID_GENERATOR: IdGenerator = input => fnv.fast1a52hex(input); - -export function createContext>( - document: T, - resolveRef: RefResolver, - generateId: IdGenerator, -): TransformerContext { - let context: AvailableContext = 'service'; +export function createContext>(document: T): TransformerContext { return { document, - get context() { - return context; - }, - set context(value) { - context = value; - if (value !== 'operation') { - this.parentId = this.ids[value]; - } - }, - maybeResolveLocalRef(target: unknown) { - if (hasRef(target) && isLocalRef(target.$ref)) { - try { - return resolveRef.call(this, target); - } catch { - return target; - } - } - - return target; - }, - generateId(value) { - this.parentId = generateId(value); - return this.parentId; - }, - ids: { - service: '', - path: '', - operation: '', - }, - parentId: '', - }; -} - -export function withContext = TranslateFunction>( - fn: F, -): F { - return function (...args: Parameters) { - const { context, parentId } = this; - const result = fn.apply(this, args); - this.context = context; - this.parentId = parentId; - return result; + maybeResolveLocalRef: maybeResolveLocalRef.bind(null, document), }; } diff --git a/src/merge.ts b/src/merge.ts index 89f30131..b007adae 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -8,8 +8,7 @@ import { IServer, } from '@stoplight/types'; import type { JSONSchema7 as JSONSchema } from 'json-schema'; - -import { isEqual } from './utils'; +import isEqual = require('lodash.isequal'); function isExclusivelyAnyOfSchema(schema: JSONSchema): schema is { anyOf: JSONSchema[] } { return !!(schema.anyOf && Object.keys(schema).length === 1); @@ -67,7 +66,6 @@ const mergeContents = mergeLists( (c1, c2) => c1.mediaType.toLowerCase() === c2.mediaType.toLowerCase(), (c1, c2) => { return { - id: c1.id, mediaType: c1.mediaType, schema: mergeTwo(mergeSchemas, c1.schema, c2.schema), examples: mergeContentExamples([c1.examples, c2.examples]), @@ -109,7 +107,6 @@ export const mergeResponses = mergeLists( function mergeRequestBodies(b1: IHttpOperationRequestBody, b2: IHttpOperationRequestBody): IHttpOperationRequestBody { return { - id: b1.id, description: [b1.description, b2.description].filter(Boolean).join('; ') || undefined, required: b1.required && b2.required, contents: mergeContents(b1.contents || [], b2.contents || []), diff --git a/src/oas/__tests__/__snapshots__/operation.test.ts.snap b/src/oas/__tests__/__snapshots__/operation.test.ts.snap index 12c6edbd..be3cd93d 100644 --- a/src/oas/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/operation.test.ts.snap @@ -11,7 +11,7 @@ Array [ }, "x-test": true, }, - "id": "9ab1c1339675f", + "id": "?http-operation-id?", "iid": "addPet", "internal": false, "method": "post", @@ -21,113 +21,22 @@ Array [ "contents": Array [ Object { "examples": Array [], - "id": "becbc0ed96d9d", "mediaType": "application/json", "schema": Object { + "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", - "properties": Object { - "category": Object { - "$ref": "#/definitions/Category", - }, - "id": Object { - "format": "int64", - "maximum": 9223372036854776000, - "minimum": -9223372036854776000, - "type": "integer", - }, - "name": Object { - "examples": Array [ - "doggie", - ], - "type": "string", - }, - "photoUrls": Object { - "items": Object { - "type": "string", - }, - "type": "array", - "xml": Object { - "name": "photoUrl", - "wrapped": true, - }, - }, - "status": Object { - "description": "pet status in the store", - "enum": Array [ - "available", - "pending", - "sold", - ], - "type": "string", - }, - }, - "required": Array [ - "name", - "photoUrls", - ], - "type": "object", - "x-stoplight-id": "2b4cfd51fbcde", - "xml": Object { - "name": "Pet", - }, }, }, Object { "examples": Array [], - "id": "7e79049c5af23", "mediaType": "application/xml", "schema": Object { + "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", - "properties": Object { - "category": Object { - "$ref": "#/definitions/Category", - }, - "id": Object { - "format": "int64", - "maximum": 9223372036854776000, - "minimum": -9223372036854776000, - "type": "integer", - }, - "name": Object { - "examples": Array [ - "doggie", - ], - "type": "string", - }, - "photoUrls": Object { - "items": Object { - "type": "string", - }, - "type": "array", - "xml": Object { - "name": "photoUrl", - "wrapped": true, - }, - }, - "status": Object { - "description": "pet status in the store", - "enum": Array [ - "available", - "pending", - "sold", - ], - "type": "string", - }, - }, - "required": Array [ - "name", - "photoUrls", - ], - "type": "object", - "x-stoplight-id": "2b4cfd51fbcde", - "xml": Object { - "name": "Pet", - }, }, }, ], "description": "Pet object that needs to be added to the store", - "id": "62e6080a56c45", "required": true, }, "cookie": Array [], @@ -141,12 +50,12 @@ Array [ "contents": Array [], "description": "Invalid input", "headers": Array [], - "id": "029110671da4a", }, ], "security": Array [ Array [ Object { + "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -156,7 +65,6 @@ Array [ }, }, }, - "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -164,12 +72,10 @@ Array [ ], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -177,7 +83,6 @@ Array [ "summary": "Add a new pet to the store", "tags": Array [ Object { - "id": "bfc2f4aa9bced", "name": "pet", }, ], @@ -185,7 +90,7 @@ Array [ Object { "deprecated": false, "extensions": Object {}, - "id": "29e0f9745e980", + "id": "?http-operation-id?", "internal": false, "method": "get", "path": "/pets", @@ -194,13 +99,11 @@ Array [ "headers": Array [ Object { "deprecated": false, - "id": "b93f4ef60cf1d", "name": "Rate-Limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "9360fa7dc92ab", }, "style": "simple", }, @@ -209,25 +112,21 @@ Array [ "query": Array [ Object { "deprecated": false, - "id": "e1c08deab8b36", "name": "skip", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "828c85d6a2307", }, "style": "form", }, Object { "deprecated": false, - "id": "d04bcf1c141cf", "name": "limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "cdfdf74babd0b", }, "style": "form", }, @@ -239,18 +138,15 @@ Array [ "contents": Array [], "description": "", "headers": Array [], - "id": "6ae68ec4738b2", }, ], "security": Array [], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -261,7 +157,7 @@ Array [ Object { "deprecated": false, "extensions": Object {}, - "id": "8bd67834e0236", + "id": "?http-operation-id?", "internal": false, "method": "options", "path": "/pets", @@ -270,13 +166,11 @@ Array [ "headers": Array [ Object { "deprecated": false, - "id": "b93f4ef60cf1d", "name": "Rate-Limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "9360fa7dc92ab", }, "style": "simple", }, @@ -285,25 +179,21 @@ Array [ "query": Array [ Object { "deprecated": false, - "id": "e1c08deab8b36", "name": "skip", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "828c85d6a2307", }, "style": "form", }, Object { "deprecated": false, - "id": "d04bcf1c141cf", "name": "limit", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "cdfdf74babd0b", }, "style": "form", }, @@ -318,20 +208,17 @@ Array [ Object { "deprecated": false, "description": "Allowed clients", - "id": "0b9c41debc794", "name": "Allow", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "b5186fbb4fdf5", }, "style": "simple", }, Object { "deprecated": false, "description": "Remaining requests", - "id": "bbf28e682b54b", "name": "X-Rate-Limit", "required": false, "schema": Object { @@ -340,23 +227,19 @@ Array [ "maximum": 9223372036854776000, "minimum": -9223372036854776000, "type": "number", - "x-stoplight-id": "a07f1d06408fd", }, "style": "simple", }, ], - "id": "ee11fabc87ff7", }, ], "security": Array [], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -368,7 +251,7 @@ Array [ "deprecated": false, "description": "", "extensions": Object {}, - "id": "4f51735d06d77", + "id": "?http-operation-id?", "internal": false, "method": "delete", "path": "/pets", @@ -384,25 +267,21 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], - "id": "5dfa6d9892b22", }, Object { "code": "404", "contents": Array [], "description": "Pet not found", "headers": Array [], - "id": "5dba6d9892c7e", }, ], "security": Array [], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -414,7 +293,7 @@ Array [ "deprecated": false, "description": "", "extensions": Object {}, - "id": "fc497ea5adb47", + "id": "?http-operation-id?", "iid": "updatePet", "internal": false, "method": "put", @@ -424,113 +303,22 @@ Array [ "contents": Array [ Object { "examples": Array [], - "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { + "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", - "properties": Object { - "category": Object { - "$ref": "#/definitions/Category", - }, - "id": Object { - "format": "int64", - "maximum": 9223372036854776000, - "minimum": -9223372036854776000, - "type": "integer", - }, - "name": Object { - "examples": Array [ - "doggie", - ], - "type": "string", - }, - "photoUrls": Object { - "items": Object { - "type": "string", - }, - "type": "array", - "xml": Object { - "name": "photoUrl", - "wrapped": true, - }, - }, - "status": Object { - "description": "pet status in the store", - "enum": Array [ - "available", - "pending", - "sold", - ], - "type": "string", - }, - }, - "required": Array [ - "name", - "photoUrls", - ], - "type": "object", - "x-stoplight-id": "2b4cfd51fbcde", - "xml": Object { - "name": "Pet", - }, }, }, Object { "examples": Array [], - "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { + "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", - "properties": Object { - "category": Object { - "$ref": "#/definitions/Category", - }, - "id": Object { - "format": "int64", - "maximum": 9223372036854776000, - "minimum": -9223372036854776000, - "type": "integer", - }, - "name": Object { - "examples": Array [ - "doggie", - ], - "type": "string", - }, - "photoUrls": Object { - "items": Object { - "type": "string", - }, - "type": "array", - "xml": Object { - "name": "photoUrl", - "wrapped": true, - }, - }, - "status": Object { - "description": "pet status in the store", - "enum": Array [ - "available", - "pending", - "sold", - ], - "type": "string", - }, - }, - "required": Array [ - "name", - "photoUrls", - ], - "type": "object", - "x-stoplight-id": "2b4cfd51fbcde", - "xml": Object { - "name": "Pet", - }, }, }, ], "description": "Pet object that needs to be added to the store", - "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -538,13 +326,11 @@ Array [ "path": Array [ Object { "deprecated": false, - "id": "1ee79143656f4", "name": "petId", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "3c1327003ffa9", }, "style": "simple", }, @@ -557,69 +343,41 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], - "id": "c12ebf63598e3", }, Object { "code": "404", "contents": Array [ Object { "examples": Array [], - "id": "3d04477dddecb", "mediaType": "application/xml", "schema": Object { + "$ref": "#/definitions/Error", "$schema": "http://json-schema.org/draft-07/schema#", - "properties": Object { - "code": Object { - "type": "string", - }, - "message": Object { - "type": "string", - }, - }, - "required": Array [ - "code", - ], - "type": "object", - "x-stoplight-id": "b3c54fc227df9", }, }, Object { "examples": Array [], - "id": "d1be05066ac36", "mediaType": "application/json", "schema": Object { + "$ref": "#/definitions/Error", "$schema": "http://json-schema.org/draft-07/schema#", - "properties": Object { - "code": Object { - "type": "string", - }, - "message": Object { - "type": "string", - }, - }, - "required": Array [ - "code", - ], - "type": "object", - "x-stoplight-id": "b3c54fc227df9", }, }, ], "description": "Our shared 404 response.", "headers": Array [], - "id": "e1deb92ee1e73", }, Object { "code": "405", "contents": Array [], "description": "Validation exception", "headers": Array [], - "id": "c15ebf6359d8c", }, ], "security": Array [ Array [ Object { + "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -629,7 +387,6 @@ Array [ }, }, }, - "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -637,12 +394,10 @@ Array [ ], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -650,7 +405,6 @@ Array [ "summary": "Update an existing pet", "tags": Array [ Object { - "id": "bfc2f4aa9bced", "name": "pet", }, ], @@ -661,7 +415,6 @@ Array [ exports[`oas operation openapi v3 1`] = ` Array [ Object { - "deprecated": false, "description": "", "extensions": Object { "x-another": Object { @@ -669,9 +422,8 @@ Array [ }, "x-test": true, }, - "id": "9ab1c1339675f", + "id": "?http-operation-id?", "iid": "addPet", - "internal": false, "method": "post", "path": "/pets", "request": Object { @@ -680,7 +432,6 @@ Array [ Object { "encodings": Array [], "examples": Array [], - "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -725,7 +476,6 @@ Array [ "photoUrls", ], "type": "object", - "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -734,7 +484,6 @@ Array [ Object { "encodings": Array [], "examples": Array [], - "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -779,7 +528,6 @@ Array [ "photoUrls", ], "type": "object", - "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -787,7 +535,6 @@ Array [ }, ], "description": "Pet object that needs to be added to the store", - "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -801,7 +548,6 @@ Array [ "contents": Array [], "description": "Invalid input", "headers": Array [], - "id": "029110671da4a", }, ], "security": Array [ @@ -816,7 +562,6 @@ Array [ }, }, }, - "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -824,12 +569,10 @@ Array [ ], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -837,22 +580,18 @@ Array [ "summary": "Add a new pet to the store", "tags": Array [ Object { - "id": "bfc2f4aa9bced", "name": "pet", }, ], }, Object { - "deprecated": false, "extensions": Object {}, - "id": "29e0f9745e980", - "internal": false, + "id": "?http-operation-id?", "method": "get", "path": "/pets", "request": Object { "body": Object { "contents": Array [], - "id": "ccf0943cc3b92", }, "cookie": Array [], "headers": Array [], @@ -862,13 +601,10 @@ Array [ "deprecated": false, "examples": Array [], "explode": false, - "id": "e1c08deab8b36", "name": "skip", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "828c85d6a2307", }, "style": "simple", }, @@ -876,13 +612,10 @@ Array [ "deprecated": false, "examples": Array [], "explode": false, - "id": "d04bcf1c141cf", "name": "limit", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "cdfdf74babd0b", }, "style": "simple", }, @@ -895,30 +628,25 @@ Array [ Object { "encodings": Array [], "examples": Array [], - "id": "61b0e53aa1cce", "mediaType": "*/*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "properties": Object {}, "type": "object", - "x-stoplight-id": "8ea4aec4c2854", }, }, ], "description": "OK", "headers": Array [], - "id": "6ae68ec4738b2", }, ], "security": Array [], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -927,12 +655,10 @@ Array [ "tags": Array [], }, Object { - "deprecated": false, "description": "", "extensions": Object {}, - "id": "fc497ea5adb47", + "id": "?http-operation-id?", "iid": "updatePet", - "internal": false, "method": "put", "path": "/pet/{petId}", "request": Object { @@ -941,7 +667,6 @@ Array [ Object { "encodings": Array [], "examples": Array [], - "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -986,7 +711,6 @@ Array [ "photoUrls", ], "type": "object", - "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -995,7 +719,6 @@ Array [ Object { "encodings": Array [], "examples": Array [], - "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1040,7 +763,6 @@ Array [ "photoUrls", ], "type": "object", - "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -1048,7 +770,6 @@ Array [ }, ], "description": "Pet object that needs to be added to the store", - "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -1058,13 +779,10 @@ Array [ "deprecated": false, "examples": Array [], "explode": false, - "id": "1ee79143656f4", "name": "petId", - "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "3c1327003ffa9", }, "style": "simple", }, @@ -1077,7 +795,6 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], - "id": "c12ebf63598e3", }, Object { "code": "404", @@ -1085,7 +802,6 @@ Array [ Object { "encodings": Array [], "examples": Array [], - "id": "edbd8b027c519", "mediaType": "*/*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -1101,20 +817,17 @@ Array [ "code", ], "type": "object", - "x-stoplight-id": "b9501a9ded2c5", }, }, ], "description": "Our shared 404 response.", "headers": Array [], - "id": "e1deb92ee1e73", }, Object { "code": "405", "contents": Array [], "description": "Validation exception", "headers": Array [], - "id": "c15ebf6359d8c", }, ], "security": Array [ @@ -1129,7 +842,6 @@ Array [ }, }, }, - "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -1137,12 +849,10 @@ Array [ ], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -1150,7 +860,6 @@ Array [ "summary": "Update an existing pet", "tags": Array [ Object { - "id": "bfc2f4aa9bced", "name": "pet", }, ], diff --git a/src/oas/__tests__/__snapshots__/service.test.ts.snap b/src/oas/__tests__/__snapshots__/service.test.ts.snap index 20fc18d1..62583794 100644 --- a/src/oas/__tests__/__snapshots__/service.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/service.test.ts.snap @@ -6,7 +6,7 @@ Object { "email": "apiteam@swagger.io", }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key \`special-key\` to test the authorization filters.", - "id": "abc", + "id": "?http-service-id?", "license": Object { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", @@ -14,6 +14,7 @@ Object { "name": "Swagger Petstore", "securitySchemes": Array [ Object { + "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -23,12 +24,11 @@ Object { }, }, }, - "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, Object { - "id": "39070f74aaf58", + "description": undefined, "in": "header", "key": "api_key", "name": "api_key", @@ -37,12 +37,10 @@ Object { ], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -50,17 +48,14 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", - "id": "bfc2f4aa9bced", "name": "pet", }, Object { "description": "Access to Petstore orders", - "id": "20c6cf86f6d6d", "name": "store", }, Object { "description": "Operations about user", - "id": "10e73f5f26c07", "name": "user", }, ], @@ -75,7 +70,7 @@ Object { "email": "apiteam@swagger.io", }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key \`special-key\` to test the authorization filters.", - "id": "abc", + "id": "?http-service-id?", "license": Object { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", @@ -92,12 +87,10 @@ Object { }, }, }, - "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, Object { - "id": "39070f74aaf58", "in": "header", "key": "api_key", "name": "api_key", @@ -106,12 +99,10 @@ Object { ], "servers": Array [ Object { - "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -119,17 +110,14 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", - "id": "bfc2f4aa9bced", "name": "pet", }, Object { "description": "Access to Petstore orders", - "id": "20c6cf86f6d6d", "name": "store", }, Object { "description": "Operations about user", - "id": "10e73f5f26c07", "name": "user", }, ], diff --git a/src/oas/__tests__/accessors.test.ts b/src/oas/__tests__/accessors.test.ts index f608f4b0..29879b23 100644 --- a/src/oas/__tests__/accessors.test.ts +++ b/src/oas/__tests__/accessors.test.ts @@ -1,116 +1,80 @@ -import { createContext, DEFAULT_ID_GENERATOR } from '../../context'; -import { Fragment } from '../../types'; -import { createOasParamsIterator } from '../accessors'; -import { resolveRef } from '../resolver'; +import { getValidOasParameters } from '../accessors'; import { OasVersion } from '../types'; -const getValidOasParameters = (document: { paths: { '/': { get: Fragment } } }, spec: OasVersion) => - Array.from( - createOasParamsIterator(spec as any).call( - createContext(document, resolveRef, DEFAULT_ID_GENERATOR), - document.paths['/'], - document.paths['/'].get, - ), - ); - -describe('createOasParamsIterator', () => { - describe.each([OasVersion.OAS2, OasVersion.OAS3])('given %s spec', spec => { - it('should return empty array', () => { - const document = { - paths: { - '/': { - get: {}, - }, - }, - }; - - expect(getValidOasParameters(document, spec)).toEqual([]); - }); - - it('should fallback to operation parameters', () => { - const document = { - paths: { - '/': { - get: { - parameters: [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, - ], - }, - }, - }, - }; - - expect(getValidOasParameters(document, spec)).toEqual([ - { - in: 'header', - name: 'n1', - }, - { - in: 'query', - name: 'n2', - }, - ]); - }); - - it('should fallback to path parameters', () => { - const document = { - paths: { - '/': { - parameters: [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, - ], - get: {}, - }, - }, - }; +describe('getOasParameters', () => { + it('should return empty array', () => { + expect(getValidOasParameters({}, OasVersion.OAS2, undefined, undefined)).toEqual([]); + }); - expect(getValidOasParameters(document, spec)).toEqual([ - { - in: 'header', - name: 'n1', - }, - { - in: 'query', - name: 'n2', - }, - ]); - }); + it('should fallback to operation parameters', () => { + expect( + getValidOasParameters( + {}, + OasVersion.OAS2, + [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, + ], + undefined, + ), + ).toEqual([ + { + in: 'header', + name: 'n1', + }, + { + in: 'query', + name: 'n2', + }, + ]); + }); - it('should prefer operation parameters', () => { - const document = { - paths: { - '/': { - parameters: [ - { name: 'n1', in: 'query', type: 'string' }, - { name: 'np3', in: 'header' }, - ], - get: { - parameters: [ - { name: 'n1', in: 'query', type: 'array' }, - { name: 'no2', in: 'header' }, - ], - }, - }, - }, - }; + it('should fallback to path parameters', () => { + expect( + getValidOasParameters({}, OasVersion.OAS2, undefined, [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, + ]), + ).toEqual([ + { + in: 'header', + name: 'n1', + }, + { + in: 'query', + name: 'n2', + }, + ]); + }); - expect(getValidOasParameters(document, spec)).toEqual([ - { - in: 'query', - name: 'n1', - type: 'array', - }, - { - in: 'header', - name: 'no2', - }, - { - in: 'header', - name: 'np3', - }, - ]); - }); + it('should prefer operation parameters', () => { + expect( + getValidOasParameters( + {}, + OasVersion.OAS3, + [ + { name: 'n1', in: 'query', type: 'array' }, + { name: 'no2', in: 'header' }, + ], + [ + { name: 'n1', in: 'query', type: 'string' }, + { name: 'np3', in: 'header' }, + ], + ), + ).toEqual([ + { + in: 'query', + name: 'n1', + type: 'array', + }, + { + in: 'header', + name: 'no2', + }, + { + in: 'header', + name: 'np3', + }, + ]); }); }); diff --git a/src/oas/__tests__/fixtures/oas2-kitchen-sink.json b/src/oas/__tests__/fixtures/oas2-kitchen-sink.json index 77415bab..3eef66d3 100644 --- a/src/oas/__tests__/fixtures/oas2-kitchen-sink.json +++ b/src/oas/__tests__/fixtures/oas2-kitchen-sink.json @@ -1,5 +1,4 @@ { - "x-stoplight-id": "abc", "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", diff --git a/src/oas/__tests__/fixtures/oas3-kitchen-sink.json b/src/oas/__tests__/fixtures/oas3-kitchen-sink.json index 14c550d3..de2dca89 100644 --- a/src/oas/__tests__/fixtures/oas3-kitchen-sink.json +++ b/src/oas/__tests__/fixtures/oas3-kitchen-sink.json @@ -1,5 +1,4 @@ { - "x-stoplight-id": "abc", "openapi": "3.0.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 44471478..2a0b0884 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -15,6 +15,6 @@ describe('oas service', () => { it('openapi v2 and v3 should transform in the same way', () => { const oas2MappedService = transformOas2Service({ document: oas2KitchenSinkJson as any }); const oas3MappedService = transformOas3Service({ document: oas3KitchenSinkJson as any }); - expect(oas2MappedService).toStrictEqual(oas3MappedService); + expect(oas2MappedService).toEqual(oas3MappedService); }); }); diff --git a/src/oas/__tests__/tags.test.ts b/src/oas/__tests__/tags.test.ts index 852c3a0a..9efcbaa5 100644 --- a/src/oas/__tests__/tags.test.ts +++ b/src/oas/__tests__/tags.test.ts @@ -1,9 +1,7 @@ -import { createContext, DEFAULT_ID_GENERATOR } from '../../context'; -import { resolveRef } from '../resolver'; +import { createContext } from '../../context'; import { translateToTags as _translateToTags } from '../tags'; -const translateToTags = (tags: unknown) => - _translateToTags.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), tags); +const translateToTags = (tags: unknown) => _translateToTags.call(createContext({}), tags); describe('translateToTags', () => { describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { @@ -15,7 +13,6 @@ describe('translateToTags', () => { it('should filter out invalid values', () => { expect(translateToTags([{}, null, 'foo'])).toStrictEqual([ { - id: expect.any(String), name: 'foo', }, ]); @@ -24,15 +21,12 @@ describe('translateToTags', () => { it('should normalize values', () => { expect(translateToTags([0, 'foo', true])).toStrictEqual([ { - id: expect.any(String), name: '0', }, { - id: expect.any(String), name: 'foo', }, { - id: expect.any(String), name: 'true', }, ]); @@ -41,15 +35,12 @@ describe('translateToTags', () => { it('should translate array of strings to tags', () => { expect(translateToTags(['a', 'b', 'c'])).toStrictEqual([ { - id: expect.any(String), name: 'a', }, { - id: expect.any(String), name: 'b', }, { - id: expect.any(String), name: 'c', }, ]); diff --git a/src/oas/accessors.ts b/src/oas/accessors.ts index 63acf593..50675aad 100644 --- a/src/oas/accessors.ts +++ b/src/oas/accessors.ts @@ -1,44 +1,50 @@ import type { Extensions } from '@stoplight/types'; -import { Fragment, TransformerContext } from '../types'; -import { entries } from '../utils'; +import { Fragment } from '../types'; +import { entries, maybeResolveLocalRef } from '../utils'; import { isValidOas2Param, isValidOas3Param, Oas2ParamBase, Oas3ParamBase, ParamBase } from './guards'; import { OasVersion } from './types'; const ROOT_EXTENSIONS = ['x-internal']; -const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; - -type OasParamsIterator = (this: TransformerContext, path: Fragment, operation: Fragment) => Iterable; - -export function createOasParamsIterator(spec: OasVersion.OAS2): OasParamsIterator; -export function createOasParamsIterator(spec: OasVersion.OAS3): OasParamsIterator; -export function createOasParamsIterator(spec: OasVersion): OasParamsIterator { - return function* (path, operation) { - const seenParams = new Set(); - const { parentId, context } = this; - const opParams = Array.isArray(operation.parameters) ? operation.parameters : []; - const params = [...opParams, ...(Array.isArray(path.parameters) ? path.parameters : [])]; - - for (let i = 0; i < params.length; i++) { - const param = this.maybeResolveLocalRef(params[i]); - if (!(spec === OasVersion.OAS2 ? isValidOas2Param : isValidOas3Param)(param)) continue; +function getParameters(document: Fragment, spec: OasVersion.OAS2, params: unknown): Oas2ParamBase[]; +function getParameters(document: Fragment, spec: OasVersion.OAS3, params: unknown): Oas3ParamBase[]; +function getParameters(document: Fragment, spec: OasVersion, params: unknown): ParamBase[] { + if (!Array.isArray(params)) return []; - const key = getIdForParameter(param); - - if (seenParams.has(key)) continue; - seenParams.add(key); - - if (this.context !== 'service') { - this.context = i < opParams.length ? 'operation' : 'path'; - } + const resolved = params.map(maybeResolveLocalRef.bind(null, document)); + return spec === OasVersion.OAS2 ? resolved.filter(isValidOas2Param) : resolved.filter(isValidOas3Param); +} - yield param; - } +const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; - this.context = context; - this.parentId = parentId; - }; +export function getValidOasParameters( + document: Fragment, + spec: OasVersion.OAS2, + operationParams: unknown, + pathParams: unknown, +): Oas2ParamBase[]; +export function getValidOasParameters( + document: Fragment, + spec: OasVersion.OAS3, + operationParams: unknown, + pathParams: unknown, +): Oas3ParamBase[]; +export function getValidOasParameters( + document: Fragment, + spec: any, + operationParams: unknown, + pathParams: unknown, +): ParamBase[] { + const uniqueParameters: Record = {}; + + const params = [...getParameters(document, spec, operationParams), ...getParameters(document, spec, pathParams)]; + + for (const param of params) { + uniqueParameters[getIdForParameter(param)] ??= param; + } + + return Object.values(uniqueParameters); } export function getExtensions(target: unknown): Extensions { diff --git a/src/oas/operation.ts b/src/oas/operation.ts index 738f67ee..94bb6864 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,14 +1,9 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, IHttpOperation } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; +import type { OpenAPIObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; -import { isString } from '../guards'; import type { HttpOperationTransformer } from '../types'; -import { TranslateFunction } from '../types'; -import { getExtensions } from './accessors'; -import { translateToTags } from './tags'; const DEFAULT_METHODS = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch', 'trace']; @@ -37,51 +32,3 @@ export function transformOasOperations( ); }); } - -function wipePathParams(p: string) { - return p.replace(/({)[^}]+(?=})/g, '$1'); -} - -export const transformOasOperation: TranslateFunction< - DeepPartial | DeepPartial, - [path: string, method: string], - Omit -> = function (path: string, method: string) { - const pathObj = this.maybeResolveLocalRef(this.document?.paths?.[path]) as PathsObject; - if (typeof pathObj !== 'object' || pathObj === null) { - throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); - } - - const operation = this.maybeResolveLocalRef(pathObj[method]) as OperationObject; - if (!operation) { - throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); - } - - const reducedPath = wipePathParams(path); - const serviceId = (this.ids.service = String(this.document['x-stoplight-id'])); - this.ids.path = this.generateId(`http_path-${this.ids.service}-${reducedPath}`); - const operationId = (this.ids.operation = this.generateId(`http_operation-${serviceId}-${method}-${reducedPath}`)); - - this.context = 'operation'; - - return { - id: operationId, - - deprecated: !!operation.deprecated, - internal: !!operation['x-internal'], - method, - path, - - tags: translateToTags.call(this, operation.tags), - extensions: getExtensions(operation), - - ...pickBy( - { - iid: operation.operationId, - description: operation.description, - summary: operation.summary, - }, - isString, - ), - }; -}; diff --git a/src/oas/resolveInlineRef.ts b/src/oas/resolveInlineRef.ts deleted file mode 100644 index 4e8a8b40..00000000 --- a/src/oas/resolveInlineRef.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { extractSourceFromRef, isPlainObject, pointerToPath } from '@stoplight/json'; -import { Dictionary } from '@stoplight/types'; - -function _resolveInlineRef(document: Dictionary, ref: string, seen: unknown[], location: string[]): unknown { - const source = extractSourceFromRef(ref); - if (source !== null) { - throw new ReferenceError('Cannot resolve external references'); - } - - const path = pointerToPath(ref); - let value: unknown = document; - for (const segment of path) { - if ((!isPlainObject(value) && !Array.isArray(value)) || !(segment in value)) { - throw new ReferenceError(`Could not resolve '${ref}'`); - } - - value = value[segment]; - - if (isPlainObject(value) && '$ref' in value) { - if (seen.includes(value)) { - // circular, let's stop - return seen[seen.length - 1]; - } - - seen.push(value); - - if (typeof value.$ref !== 'string') { - throw new TypeError('$ref should be a string'); - } - - location.length = 0; - value = _resolveInlineRef(document, value.$ref, seen, location); - } else { - location.push(String(segment)); - } - } - - if (seen.length === 0) { - return value; - } - - const sourceDocument = seen[seen.length - 1]; - - if (isPlainObject(sourceDocument) && ('summary' in sourceDocument || 'description' in sourceDocument)) { - return { - ...(value as object), - ...('description' in sourceDocument ? { description: sourceDocument.description } : null), - ...('summary' in sourceDocument ? { summary: sourceDocument.summary } : null), - }; - } - - return value; -} - -export function resolveInlineRef(document: Dictionary, ref: string, location: string[] = []): unknown { - return _resolveInlineRef(document, ref, [], location); -} diff --git a/src/oas/resolver.ts b/src/oas/resolver.ts deleted file mode 100644 index 07da3ab7..00000000 --- a/src/oas/resolver.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { JsonPath } from '@stoplight/types'; - -import type { AvailableContext, RefResolver } from '../types'; -import { resolveInlineRef } from './resolveInlineRef'; - -function inferContext(path: JsonPath): AvailableContext { - if (path.length < 2 || path[0] !== 'paths') return 'service'; - if (path.length === 2 || path[3] === 'parameters' || path[3] === 'servers') return 'path'; - return 'operation'; -} - -const SHARED_COMPONENTS_KEYS = new WeakMap(); - -export function getSharedKey(value: object) { - return SHARED_COMPONENTS_KEYS.get(value); -} - -export const resolveRef: RefResolver = function (target) { - const location: string[] = []; - const resolved = resolveInlineRef(this.document, target.$ref, location); - - const context = inferContext(location); - if (context !== null && this.context !== context) { - this.context = context; - } - - if (typeof resolved === 'object' && resolved !== null && context === 'service') { - SHARED_COMPONENTS_KEYS.set(resolved, location[0] === 'components' ? location[2] : location[1]); - } - - return resolved; -}; diff --git a/src/oas/service.ts b/src/oas/service.ts index c8fc8b81..c6cb6743 100644 --- a/src/oas/service.ts +++ b/src/oas/service.ts @@ -13,12 +13,9 @@ import { translateLogo } from './transformers/translateLogo'; export const transformOasService: TranslateFunction | DeepPartial, [], IHttpService> = function () { const document = this.document; - const id = String(document['x-stoplight-id']); - this.ids.service = id; - this.parentId = id; const httpService: IHttpService = { - id, + id: '?http-service-id?', version: document.info?.version ?? '', name: document.info?.title ?? 'no-title', diff --git a/src/oas/tags.ts b/src/oas/tags.ts index d74a78b4..7f1ec25d 100644 --- a/src/oas/tags.ts +++ b/src/oas/tags.ts @@ -1,22 +1,21 @@ import { isPlainObject } from '@stoplight/json'; import type { INodeTag, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); -import { withContext } from '../context'; + import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; import type { ArrayCallbackParameters, Fragment, TranslateFunction } from '../types'; -const translateTag = withContext, Optional>>( - function (tag) { - if (tag === null || !isSerializablePrimitive(tag)) return; - - const name = String(tag); +export const translateTag: TranslateFunction< + Fragment, + ArrayCallbackParameters, + Optional +> = function (tag) { + if (tag === null || !isSerializablePrimitive(tag)) return; - return { - id: this.generateId(`tag-${this.ids.service}-${name}`), - name, - }; - }, -); + return { + name: String(tag), + }; +}; export const translateTagDefinition: TranslateFunction< Fragment, diff --git a/src/oas/transformers/examples.ts b/src/oas/transformers/examples.ts deleted file mode 100644 index 99b1dc70..00000000 --- a/src/oas/transformers/examples.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DeepPartial, INodeExample } from '@stoplight/types'; -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; - -import { withContext } from '../../context'; -import { TranslateFunction } from '../../types'; - -export const translateToDefaultExample = withContext< - TranslateFunction, [key: string, value: unknown], INodeExample> ->(function (key, value) { - const resolvedValue = this.maybeResolveLocalRef(value); - - return { - id: this.generateId(`example-${this.parentId}-${key}`), - value: resolvedValue, - key, - }; -}); diff --git a/src/oas/transformers/schema/__tests__/schema.spec.ts b/src/oas/transformers/schema/__tests__/schema.spec.ts index cca91501..bea202ba 100644 --- a/src/oas/transformers/schema/__tests__/schema.spec.ts +++ b/src/oas/transformers/schema/__tests__/schema.spec.ts @@ -1,10 +1,8 @@ -import { createContext, DEFAULT_ID_GENERATOR } from '../../../../context'; -import { resolveRef } from '../../../resolver'; +import { createContext } from '../../../../context'; import { translateSchemaObject } from '..'; import type { OASSchemaObject } from '../types'; -const translate = (schemaObject: OASSchemaObject) => - translateSchemaObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), schemaObject); +const translate = (schemaObject: OASSchemaObject) => translateSchemaObject.call(createContext({}), schemaObject); describe('translateSchemaObject', () => { it('should translate id', () => { @@ -18,7 +16,6 @@ describe('translateSchemaObject', () => { }, }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -39,7 +36,6 @@ describe('translateSchemaObject', () => { exclusiveMaximum: true, }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', exclusiveMinimum: 2, @@ -72,7 +68,6 @@ describe('translateSchemaObject', () => { ], }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', anyOf: [ { @@ -115,7 +110,6 @@ describe('translateSchemaObject', () => { }, }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { exclusiveMinimum: { @@ -158,7 +152,6 @@ describe('translateSchemaObject', () => { 'x-nullable': true, }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: ['string', 'null'], }); @@ -171,7 +164,6 @@ describe('translateSchemaObject', () => { 'x-example': 'Cat', }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', examples: ['Cat'], @@ -187,7 +179,6 @@ describe('translateSchemaObject', () => { nullable: true, }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: ['string', 'null'], }); @@ -200,7 +191,6 @@ describe('translateSchemaObject', () => { example: 'Cat', }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', examples: ['Cat'], @@ -214,7 +204,6 @@ describe('translateSchemaObject', () => { format: 'base64', }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', contentEncoding: 'base64', @@ -228,7 +217,6 @@ describe('translateSchemaObject', () => { format: 'binary', }), ).toStrictEqual({ - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', contentMediaType: 'application/octet-stream', diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index 029d1159..cd12e29c 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -4,9 +4,7 @@ import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; import type { OpenAPIObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; -import { withContext } from '../../../context'; -import { TranslateFunction } from '../../../types'; -import { getSharedKey } from '../../resolver'; +import type { TranslateFunction } from '../../../types'; import keywords from './keywords'; import type { OASSchemaObject } from './types'; @@ -16,46 +14,28 @@ type InternalOptions = { structs: string[]; }; -const CACHE = new WeakMap(); - // Convert from OpenAPI 2.0, OpenAPI 3.0 `SchemaObject` or JSON Schema Draft4/6 to JSON Schema Draft 7 // This converter shouldn't make any differences to Schema objects defined in OpenAPI 3.1, excepts when jsonSchemaDialect is provided. -export const translateSchemaObject = withContext< - TranslateFunction< - DeepPartial, - [schema: OASSchemaObject], - JSONSchema7 - > ->(function (schema) { +export const translateSchemaObject: TranslateFunction< + DeepPartial, + [schema: OASSchemaObject], + JSONSchema7 +> = function (schema) { const document = this.document; - const resolvedSchema = this.maybeResolveLocalRef(schema); - if (!isPlainObject(resolvedSchema)) return {}; - let cached = CACHE.get(resolvedSchema); - if (cached) { - return { ...cached }; - } - - const actualKey = this.context === 'service' ? getSharedKey(resolvedSchema) : ''; - const id = this.generateId(`schema-${this.parentId}-${actualKey}`); if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { - cached = { + return { $schema: document.jsonSchemaDialect, // let's assume it's draft 7, albeit it might be draft 2020-12 or 2019-09. // it's a safe bet, because there was only _one_ relatively minor breaking change introduced between Draft 7 and 2020-12. - ...(resolvedSchema as JSONSchema7), - 'x-stoplight-id': id, + ...(schema as JSONSchema7), }; - } else { - cached = convertSchema(resolvedSchema); - cached['x-stoplight-id'] = id; } - CACHE.set(resolvedSchema, cached); - return cached; -}); + return convertSchema(schema); +}; -export function convertSchema(schema: OASSchemaObject): JSONSchema7 { +export function convertSchema(schema: OASSchemaObject) { const clonedSchema = _convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); diff --git a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap index be67c303..d58bd9bb 100644 --- a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap @@ -5,7 +5,7 @@ Object { "deprecated": true, "description": "odesc", "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "oid", "internal": false, "method": "get", @@ -23,12 +23,10 @@ Object { Object { "examples": Array [ Object { - "id": Any, "key": "example", "value": Object {}, }, ], - "id": Any, "mediaType": "", "schema": Object {}, }, @@ -37,23 +35,21 @@ Object { "headers": Array [ Object { "deprecated": false, - "id": Any, "name": "header", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "integer", - "x-stoplight-id": Any, }, "style": "simple", }, ], - "id": Any, }, ], "security": Array [ Array [ Object { + "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -62,14 +58,13 @@ Object { }, }, }, - "id": Any, "key": "petstore_auth", "type": "oauth2", }, ], Array [ Object { - "id": Any, + "description": undefined, "in": "header", "key": "api_key", "name": "api_key_name", diff --git a/src/oas2/__tests__/guards.test.ts b/src/oas2/__tests__/guards.test.ts new file mode 100644 index 00000000..67051a85 --- /dev/null +++ b/src/oas2/__tests__/guards.test.ts @@ -0,0 +1,39 @@ +import { isSecurityScheme, isTagObject } from '../guards'; + +describe('guards', () => { + describe('isSecurityScheme()', () => { + it('should return true for scheme with type', () => { + expect( + isSecurityScheme({ + type: 'apiKey', + }), + ).toEqual(true); + }); + + it('should return false for non object', () => { + expect(isSecurityScheme(undefined)).toEqual(false); + }); + + it('should return false for missing type property', () => { + expect(isSecurityScheme({ foo: 'bar' })).toEqual(false); + }); + }); + + describe('isTagObject()', () => { + it('should return true for scheme with name', () => { + expect( + isTagObject({ + name: 'foo', + }), + ).toEqual(true); + }); + + it('should return false for non object', () => { + expect(isTagObject(undefined)).toEqual(false); + }); + + it('should return false for missing name property', () => { + expect(isTagObject({ foo: 'bar' })).toEqual(false); + }); + }); +}); diff --git a/src/oas2/__tests__/operation.test.ts b/src/oas2/__tests__/operation.test.ts index a91f0583..7e010fef 100644 --- a/src/oas2/__tests__/operation.test.ts +++ b/src/oas2/__tests__/operation.test.ts @@ -68,44 +68,7 @@ describe('transformOas2Operation', () => { method: 'get', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - responses: [ - { - id: expect.any(String), - contents: [ - { - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - ], - }, - ], - headers: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - ], - }, - ], - security: [ - [ - { - id: expect.any(String), - }, - ], - [ - { - id: expect.any(String), - }, - ], - ], - }); + ).toMatchSnapshot(); }); it('should properly translate operation with no response body', () => { @@ -192,7 +155,7 @@ describe('transformOas2Operation', () => { extensions: {}, }), { - id: expect.any(String), + id: '?http-operation-id?', deprecated: false, internal: false, path: '/users/{userId}', @@ -237,7 +200,7 @@ describe('transformOas2Operation', () => { document, }), ).toStrictEqual({ - id: expect.any(String), + id: '?http-operation-id?', deprecated: false, internal: false, method: 'get', @@ -246,12 +209,10 @@ describe('transformOas2Operation', () => { cookie: [], headers: [ { - id: expect.any(String), name: 'name', required: false, deprecated: false, schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', }, style: 'simple', diff --git a/src/oas2/__tests__/service.test.ts b/src/oas2/__tests__/service.test.ts index 161e5067..754cd2f5 100644 --- a/src/oas2/__tests__/service.test.ts +++ b/src/oas2/__tests__/service.test.ts @@ -1,13 +1,11 @@ import { OpenAPIObject } from 'openapi3-ts'; import { Spec } from 'swagger-schema-official'; -import { Fragment } from '../../types'; import { transformOas2Service } from '../service'; describe('oas2 service', () => { it('should handle non array schemes', () => { - const document: Partial & Fragment = { - 'x-stoplight-id': 'def', + const document: Partial = { schemes: 2 as any, }; @@ -16,15 +14,14 @@ describe('oas2 service', () => { document, }), ).toStrictEqual({ - id: 'def', + id: '?http-service-id?', name: 'no-title', version: '', }); }); it('should accept empty title', () => { - const document: Partial & Fragment = { - 'x-stoplight-id': 'abc', + const document: Partial = { host: 'petstore.swagger.io', basePath: '/v2', info: { @@ -39,17 +36,15 @@ describe('oas2 service', () => { document, }), ).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: '', version: '1.0', servers: [ { - id: expect.any(String), name: '', url: 'https://petstore.swagger.io/v2', }, { - id: expect.any(String), name: '', url: 'http://petstore.swagger.io/v2', }, @@ -58,22 +53,20 @@ describe('oas2 service', () => { }); it('should handle invalid document securities gracefully', () => { - const document: Partial & Fragment = { - 'x-stoplight-id': 'abc', + const document: Partial = { securityDefinitions: {}, security: ['API-Key'] as any, }; expect(transformOas2Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '', }); }); it('filters out scopes', () => { - const document: Partial & Fragment = { - 'x-stoplight-id': 'def', + const document: Partial = { swagger: '2.0', securityDefinitions: { 'API Key': { @@ -95,7 +88,6 @@ describe('oas2 service', () => { describe('x-logo support', () => { it('should support x-logo', () => { const document: Partial = { - 'x-stoplight-id': 'abc', info: { title: 'no-title', version: '1.0.0', @@ -109,7 +101,7 @@ describe('oas2 service', () => { }; expect(transformOas2Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '1.0.0', logo: { @@ -122,7 +114,6 @@ describe('oas2 service', () => { }); it('should provide default values for href and altText', () => { const document: Partial = { - 'x-stoplight-id': 'abc', info: { title: 'no-title', version: '1.0.0', @@ -134,7 +125,7 @@ describe('oas2 service', () => { }; expect(transformOas2Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', contact: { url: 'https://stoplight.io', diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index 1230a42b..bbcfe522 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial } from '@stoplight/types'; -import type { Operation, Security, Spec } from 'swagger-schema-official'; +import { DeepPartial, Dictionary } from '@stoplight/types'; +import { Operation, Schema, Security, Spec } from 'swagger-schema-official'; import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../guards'; @@ -68,11 +68,11 @@ function getProducesOrConsumes( return mimeTypes.flat().filter(isString); } -export function getExamplesFromSchema(data: unknown): Record { +export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Record { if (!isPlainObject(data)) return {}; return { - ...(isPlainObject(data['x-examples']) && { ...data['x-examples'] }), + ...('x-examples' in data && isPlainObject(data['x-examples']) && { ...data['x-examples'] }), ...('example' in data && { default: data.example }), }; } diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index cdfc6da3..bf5d124f 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -33,11 +33,3 @@ export function isPathParam(param: unknown): param is Oas2ParamBase & { in: 'pat export function isHeaderParam(param: unknown): param is Oas2ParamBase & { in: 'header' } { return isValidOas2Param(param) && param.in === 'header'; } - -export function isBodyParam(param: unknown): param is Oas2ParamBase & { in: 'body' } { - return isValidOas2Param(param) && param.in === 'body'; -} - -export function isFormDataParam(param: unknown): param is Oas2ParamBase & { in: 'formData' } { - return isValidOas2Param(param) && param.in === 'formData'; -} diff --git a/src/oas2/index.ts b/src/oas2/index.ts index f2e98a7a..0e972888 100644 --- a/src/oas2/index.ts +++ b/src/oas2/index.ts @@ -1,2 +1,3 @@ export * from './operation'; export * from './service'; +export * from './transformers/params'; diff --git a/src/oas2/operation.ts b/src/oas2/operation.ts index 0ee05d52..30e022a3 100644 --- a/src/oas2/operation.ts +++ b/src/oas2/operation.ts @@ -1,32 +1,59 @@ -import type { DeepPartial, IHttpOperation } from '@stoplight/types'; +import { isPlainObject } from '@stoplight/json'; +import type { IHttpOperation } from '@stoplight/types'; import type { Spec } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); -import { createContext, DEFAULT_ID_GENERATOR } from '../context'; -import { transformOasOperation, transformOasOperations } from '../oas/operation'; -import { resolveRef } from '../oas/resolver'; +import { createContext } from '../context'; +import { isString } from '../guards'; +import { getExtensions } from '../oas/accessors'; +import { transformOasOperations } from '../oas/operation'; +import { translateToTags } from '../oas/tags'; import { Oas2HttpOperationTransformer } from '../oas/types'; -import type { Fragment } from '../types'; +import { maybeResolveLocalRef } from '../utils'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; import { translateToServers } from './transformers/servers'; -export function transformOas2Operations(document: DeepPartial): IHttpOperation[] { +export function transformOas2Operations(document: Spec): IHttpOperation[] { return transformOasOperations(document, transformOas2Operation); } -export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document: _document, path, method }) => { - const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); - const httpOperation = transformOasOperation.call(ctx, path, method); - const pathObj = ctx.maybeResolveLocalRef(ctx.document.paths![path]) as Fragment; - const operation = ctx.maybeResolveLocalRef(pathObj[method]) as Fragment; +export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document, path, method }) => { + const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]); + if (!isPlainObject(pathObj)) { + throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); + } + + const operation = maybeResolveLocalRef(document, pathObj[method]); + if (!isPlainObject(operation)) { + throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); + } + + const ctx = createContext(document); return { - ...httpOperation, + id: '?http-operation-id?', + method, + path, + + deprecated: !!operation.deprecated, + internal: !!operation['x-internal'], responses: translateToResponses.call(ctx, operation), servers: translateToServers.call(ctx, operation), request: translateToRequest.call(ctx, pathObj, operation), + tags: translateToTags.call(ctx, operation.tags), security: translateToSecurities.call(ctx, operation.security), + extensions: getExtensions(operation), + + ...pickBy( + { + iid: operation.operationId, + description: operation.description, + summary: operation.summary, + }, + isString, + ), }; }; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index 344282f5..a67f48d6 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,9 +1,8 @@ import { isPlainObject } from '@stoplight/json'; import pickBy = require('lodash.pickby'); -import { createContext, DEFAULT_ID_GENERATOR } from '../context'; +import { createContext } from '../context'; import { isNonNullable, isString } from '../guards'; -import { resolveRef } from '../oas/resolver'; import { transformOasService } from '../oas/service'; import { Oas2HttpServiceTransformer } from '../oas/types'; import { entries } from '../utils'; @@ -11,7 +10,7 @@ import { translateToSingleSecurity } from './transformers/securities'; import { translateToServer } from './transformers/servers'; export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document, resolveRef, DEFAULT_ID_GENERATOR); + const ctx = createContext(document); const httpService = transformOasService.call(ctx); if (document.info?.license) { diff --git a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap index a2a2dca2..5c25ea5f 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap @@ -6,31 +6,26 @@ Object { Object { "examples": Array [ Object { - "id": Any, "key": "example-1", "value": Object { "hello": "world", }, }, Object { - "id": Any, "key": "example-2", "value": Object { "foo": "bar", }, }, ], - "id": Any, "mediaType": "*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "e-mail", - "x-stoplight-id": "5d6bd328630ce", }, }, ], "description": "descr", - "id": Any, "required": true, } `; @@ -40,17 +35,14 @@ Object { "contents": Array [ Object { "examples": Array [], - "id": Any, "mediaType": "*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "e-mail", - "x-stoplight-id": Any, }, }, ], "description": "descr", - "id": Any, "required": true, } `; @@ -59,14 +51,12 @@ exports[`params.translator translateToHeaderParam should translate header param Object { "deprecated": false, "description": "desc", - "id": Any, "name": "name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "minimum": 12, "type": "integer", - "x-stoplight-id": "e6c28cf6b8000", }, "style": "simple", } @@ -79,26 +69,22 @@ Array [ Object { "deprecated": false, "description": "a description", - "id": "117996bbb6f45", "name": "header-name", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "9259b14a1615b", }, "style": "simple", }, Object { "deprecated": false, "description": "another description", - "id": "dce8b374eb7d6", "name": "plain-tex", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "31f792c907f10", }, "style": "simple", }, @@ -110,13 +96,11 @@ Array [ Object { "deprecated": false, "description": "a description", - "id": "117996bbb6f45", "name": "header-name", "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": "9259b14a1615b", }, "style": "simple", }, @@ -127,13 +111,11 @@ exports[`params.translator translateToPathParameter should translate 1`] = ` Object { "deprecated": false, "description": "descr", - "id": Any, "name": "name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", - "x-stoplight-id": Any, }, "style": "simple", } diff --git a/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap index 07b121c2..88edbf9d 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -1,135 +1,135 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`responses should translate to multiple responses 1`] = ` -Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [ - Object { - "id": "3983e02fea0fe", - "key": "application/json", - "value": Object {}, +Array [ + Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [ + Object { + "key": "application/json", + "value": Object {}, + }, + ], + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", }, - ], - "id": Any, - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, - }, - Object { - "examples": Array [], - "id": Any, - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, + Object { + "examples": Array [], + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, }, - }, - ], - "description": "d1", - "headers": Array [], - "id": Any, -} -`; - -exports[`responses should translate to multiple responses 2`] = ` -Object { - "code": "r2", - "contents": Array [ - Object { - "examples": Array [], - "id": Any, - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, + ], + "description": "d1", + "headers": Array [ + Object { + "name": "fake-header", + "style": "simple", }, - }, - Object { - "examples": Array [ - Object { - "id": Any, - "key": "application/xml", - "value": Object {}, + ], + }, + Object { + "code": "r2", + "contents": Array [ + Object { + "examples": Array [], + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", }, - ], - "id": Any, - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, - }, - ], - "description": "d2", - "headers": Array [], - "id": Any, -} + Object { + "examples": Array [ + Object { + "key": "application/xml", + "value": Object {}, + }, + ], + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, + }, + ], + "description": "d2", + "headers": Array [ + Object { + "name": "fake-header", + "style": "simple", + }, + ], + }, +] `; exports[`responses should translate to response w/o examples 1`] = ` -Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [], - "id": Any, - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, +Array [ + Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [], + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, + }, + Object { + "examples": Array [], + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, }, - }, - Object { - "examples": Array [], - "id": Any, - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, + ], + "description": "d1", + "headers": Array [ + Object { + "name": "fake-header", + "style": "simple", }, - }, - ], - "description": "d1", - "headers": Array [], - "id": Any, -} + ], + }, +] `; exports[`responses should translate to response w/o headers 1`] = ` -Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [], - "id": Any, - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, +Array [ + Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [], + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, }, - }, - Object { - "examples": Array [ - Object { - "id": Any, - "key": "application/xml", - "value": Object {}, + Object { + "examples": Array [ + Object { + "key": "application/xml", + "value": Object {}, + }, + ], + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", }, - ], - "id": Any, - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, - }, - ], - "description": "d1", - "headers": Array [], - "id": Any, -} + ], + "description": "d1", + "headers": Array [ + Object { + "name": "fake-header", + "style": "simple", + }, + ], + }, +] `; diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index d3cf309c..2b8158a8 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -1,8 +1,7 @@ import { DeepPartial, HttpParamStyles } from '@stoplight/types'; import { FormDataParameter, QueryParameter, Spec } from 'swagger-schema-official'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateFromFormDataParameters as _translateFromFormDataParameters, translateToBodyParameter as _translateToBodyParameter, @@ -15,28 +14,28 @@ import { const translateFromFormDataParameters = ( document: DeepPartial, ...params: Parameters -) => _translateFromFormDataParameters.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); +) => _translateFromFormDataParameters.call(createContext(document), ...params); const translateToBodyParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToBodyParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); +) => _translateToBodyParameter.call(createContext(document), ...params); const translateToHeaderParam = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParam.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); + _translateToHeaderParam.call(createContext(document), ...params); const translateToHeaderParams = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParams.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); + _translateToHeaderParams.call(createContext(document), ...params); const translateToPathParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToPathParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); +) => _translateToPathParameter.call(createContext(document), ...params); const translateToQueryParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToQueryParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); +) => _translateToQueryParameter.call(createContext(document), ...params); describe('params.translator', () => { let consumes = ['*']; @@ -55,9 +54,7 @@ describe('params.translator', () => { description: 'desc', }, ), - ).toMatchSnapshot({ - id: expect.any(String), - }); + ).toMatchSnapshot(); }); }); @@ -115,17 +112,7 @@ describe('params.translator', () => { }, consumes, ), - ).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - ], - }); + ).toMatchSnapshot(); }); it('should preserve readOnly flag in schema', () => { @@ -146,13 +133,7 @@ describe('params.translator', () => { ).toEqual( expect.objectContaining({ contents: expect.arrayContaining([ - expect.objectContaining({ - schema: { - 'x-stoplight-id': expect.any(String), - $schema: 'http://json-schema.org/draft-07/schema#', - readOnly: true, - }, - }), + expect.objectContaining({ schema: { $schema: 'http://json-schema.org/draft-07/schema#', readOnly: true } }), ]), }), ); @@ -182,22 +163,7 @@ describe('params.translator', () => { }, consumes, ), - ).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - { - id: expect.any(String), - }, - ], - }, - ], - }); + ).toMatchSnapshot(); }); describe('schema examples', () => { @@ -228,8 +194,8 @@ describe('params.translator', () => { contents: expect.arrayContaining([ expect.objectContaining({ examples: [ - { id: expect.any(String), key: 'example-1', value: { hello: 'world' } }, - { id: expect.any(String), key: 'example-2', value: { foo: 'bar' } }, + { key: 'example-1', value: { hello: 'world' } }, + { key: 'example-2', value: { foo: 'bar' } }, ], }), ]), @@ -266,7 +232,7 @@ describe('params.translator', () => { expect.objectContaining({ contents: expect.arrayContaining([ expect.objectContaining({ - examples: [{ id: expect.any(String), key: 'example-2', value: { foo: 'bar' } }], + examples: [{ key: 'example-2', value: { foo: 'bar' } }], }), ]), }), @@ -318,7 +284,6 @@ describe('params.translator', () => { it('converts parameters into schema', () => { const expectedContent = { - id: expect.any(String), encodings: [ { explode: true, @@ -327,11 +292,9 @@ describe('params.translator', () => { }, ], schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { arr: { - 'x-stoplight-id': expect.any(String), description: 'desc', items: { type: 'number', @@ -341,14 +304,12 @@ describe('params.translator', () => { type: 'array', }, int: { - 'x-stoplight-id': expect.any(String), description: 'desc', maximum: 3, minimum: 0, type: 'integer', }, str: { - 'x-stoplight-id': expect.any(String), minLength: 1, default: '25-07-2019', description: 'desc', @@ -368,7 +329,6 @@ describe('params.translator', () => { consumes, ), ).toEqual({ - id: expect.any(String), contents: [ Object.assign({}, expectedContent, { mediaType: 'application/x-www-form-urlencoded' }), Object.assign({}, expectedContent, { mediaType: 'multipart/form-data' }), @@ -418,12 +378,7 @@ describe('params.translator', () => { type: 'string', }, ), - ).toMatchSnapshot({ - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); }); }); diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index a79c060d..286e6788 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -6,8 +6,7 @@ import { QueryParameter, } from 'swagger-schema-official'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -20,7 +19,7 @@ import { translateToRequest as _translateToRequest } from '../request'; jest.mock('../params'); const translateToRequest = (path: Record, parameters: any[]) => { - const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }, resolveRef, DEFAULT_ID_GENERATOR); + const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }); return _translateToRequest.call(ctx, path, { parameters }); }; diff --git a/src/oas2/transformers/__tests__/responses.test.ts b/src/oas2/transformers/__tests__/responses.test.ts index eb29f18f..11881d59 100644 --- a/src/oas2/transformers/__tests__/responses.test.ts +++ b/src/oas2/transformers/__tests__/responses.test.ts @@ -1,16 +1,23 @@ -import { DeepPartial } from '@stoplight/types'; +import { DeepPartial, HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; import { Operation, Schema, Spec } from 'swagger-schema-official'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; +import { translateToHeaderParams } from '../params'; import { translateToResponses as _translateToResponses } from '../responses'; +jest.mock('../params'); + const translateToResponses = (document: DeepPartial, responses: DeepPartial) => - _translateToResponses.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), { responses }); + _translateToResponses.call(createContext(document), { responses }); describe('responses', () => { + const fakeHeaderParams: IHttpHeaderParam[] = [{ name: 'fake-header', style: HttpParamStyles.Simple }]; const produces = ['application/json', 'application/xml']; + beforeEach(() => { + (translateToHeaderParams as jest.Mock).mockReturnValue(fakeHeaderParams); + }); + it('should translate to multiple responses', () => { const responses = translateToResponses( { produces }, @@ -34,117 +41,38 @@ describe('responses', () => { }, ); - expect(responses).toHaveLength(2); - expect(responses[0]).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - examples: [], - }, - ], - }); - expect(responses[1]).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - examples: [ - { - id: expect.any(String), - }, - ], - }, - ], - }); + expect(responses).toMatchSnapshot(); }); it('should translate to response w/o headers', () => { - const responses = translateToResponses( - { produces }, - { - r1: { - description: 'd1', - examples: { - 'application/xml': {}, - }, - schema: {}, - }, - }, - ); - - expect(responses).toHaveLength(1); - expect(responses[0]).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, + expect( + translateToResponses( + { produces }, { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - examples: [ - { - id: expect.any(String), + r1: { + description: 'd1', + examples: { + 'application/xml': {}, }, - ], + schema: {}, + }, }, - ], - }); + ), + ).toMatchSnapshot(); }); it('should translate to response w/o examples', () => { - const responses = translateToResponses( - { produces }, - { - r1: { - description: 'd1', - schema: {}, - }, - }, - ); - - expect(responses).toHaveLength(1); - expect(responses[0]).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, + expect( + translateToResponses( + { produces }, { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), + r1: { + description: 'd1', + schema: {}, }, }, - ], - }); + ), + ).toMatchSnapshot(); }); describe('should keep foreign examples', () => { @@ -189,9 +117,7 @@ describe('responses', () => { }, }, ); - expect(responses[0].contents![0]).toHaveProperty('examples', [ - { id: expect.any(String), key: 'default', value: { name: 'value' } }, - ]); + expect(responses[0].contents![0]).toHaveProperty('examples', [{ key: 'default', value: { name: 'value' } }]); }); }); @@ -217,8 +143,8 @@ describe('responses', () => { }, ); expect(responses[0].contents![0]).toHaveProperty('examples', [ - { id: expect.any(String), key: 'application/json', value: { name: 'examples value' } }, - { id: expect.any(String), key: 'default', value: { name: 'example value' } }, + { key: 'application/json', value: { name: 'examples value' } }, + { key: 'default', value: { name: 'example value' } }, ]); }); }); @@ -245,8 +171,8 @@ describe('responses', () => { ); expect(responses[0].contents![0].examples).toHaveLength(2); expect(responses[0].contents![0].examples).toEqual([ - { id: expect.any(String), key: 'application/json', value: {} }, - { id: expect.any(String), key: 'application/i-have-no-clue', value: {} }, + { key: 'application/json', value: {} }, + { key: 'application/i-have-no-clue', value: {} }, ]); }); }); diff --git a/src/oas2/transformers/__tests__/securities.test.ts b/src/oas2/transformers/__tests__/securities.test.ts index c47e799d..9c60c069 100644 --- a/src/oas2/transformers/__tests__/securities.test.ts +++ b/src/oas2/transformers/__tests__/securities.test.ts @@ -1,12 +1,11 @@ import { DeepPartial } from '@stoplight/types'; import { Spec } from 'swagger-schema-official'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateToSecurities as _translateToSecurities } from '../securities'; const translateToSecurities = (document: DeepPartial, ...params: Parameters) => - _translateToSecurities.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); + _translateToSecurities.call(createContext(document), ...params); describe('securities', () => { describe('translateToSecurities', () => { @@ -41,7 +40,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', @@ -69,7 +67,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), name: 'a name', type: 'apiKey', in: 'header', @@ -97,7 +94,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), name: '', type: 'apiKey', in: 'header', @@ -144,7 +140,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-flow-security', @@ -172,7 +167,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: {} } }, key: 'implicit-flow-security', @@ -201,7 +195,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { password: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'password-flow-security', @@ -230,7 +223,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { clientCredentials: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'application-flow-security', @@ -260,7 +252,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { authorizationCode: { @@ -326,7 +317,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', @@ -335,7 +325,6 @@ describe('securities', () => { ], [ { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', @@ -344,7 +333,6 @@ describe('securities', () => { ], [ { - id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', @@ -361,21 +349,18 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', key: 'basic-security', }, { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', type: 'oauth2', }, { - id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 0f10b8bf..11cd45d6 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,14 +1,13 @@ import type { DeepPartial } from '@stoplight/types'; import type { Spec } from 'swagger-schema-official'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateToServers as _translateToServers } from '../servers'; type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; const translateToServers = (document: DeepPartial, ...params: Parameters) => - _translateToServers.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); + _translateToServers.call(createContext(document), ...params); describe('translateToServers', () => { afterAll(() => { @@ -21,7 +20,7 @@ describe('translateToServers', () => { href: 'https://www.someotherdomain.com?query=123', }; expect(translateToServers({ host: 'stoplight.io' }, { schemes: ['http'] })).toEqual([ - { id: expect.any(String), url: 'http://stoplight.io' }, + { url: 'http://stoplight.io' }, ]); }); @@ -30,11 +29,9 @@ describe('translateToServers', () => { translateToServers({ host: 'stoplight.io', basePath: '/base-path' }, { schemes: ['http', 'https'] }), ).toEqual([ { - id: expect.any(String), url: 'http://stoplight.io/base-path', }, { - id: expect.any(String), url: 'https://stoplight.io/base-path', }, ]); @@ -52,11 +49,9 @@ describe('translateToServers', () => { ), ).toEqual([ { - id: expect.any(String), url: 'http://stoplight.io/base-path', }, { - id: expect.any(String), url: 'https://stoplight.io/base-path', }, ]); @@ -67,7 +62,6 @@ describe('translateToServers', () => { translateToServers({ schemes: ['https'], host: 'stoplight.io', basePath: '/base-path' }, { schemes: ['http'] }), ).toEqual([ { - id: expect.any(String), url: 'http://stoplight.io/base-path', }, ]); @@ -80,7 +74,6 @@ describe('translateToServers', () => { it('given no basePath should return servers', () => { expect(translateToServers({ schemes: ['http'], host: 'stoplight.io' }, {})).toEqual([ { - id: expect.any(String), url: 'http://stoplight.io', }, ]); @@ -121,11 +114,9 @@ describe('translateToServers', () => { it('should handle invalid server basePath gracefully', () => { expect(translateToServers({ host: 'stoplight.io', basePath: 123 as any }, { schemes: ['http', 'https'] })).toEqual([ { - id: expect.any(String), url: 'http://stoplight.io', }, { - id: expect.any(String), url: 'https://stoplight.io', }, ]); diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index c4c00cdf..f1cb5cda 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -18,14 +18,11 @@ import type { PathParameter, QueryParameter, } from 'swagger-schema-official'; - -import { withContext } from '../../context'; import pickBy = require('lodash.pickby'); import pick = require('lodash.pick'); import { isBoolean, isNonNullable, isString } from '../../guards'; import { Oas2ParamBase } from '../../oas/guards'; -import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; @@ -64,19 +61,17 @@ function chooseQueryParameterStyle( } } -export const translateToHeaderParam = withContext< - Oas2TranslateFunction<[param: DeepPartial & Oas2ParamBase & { in: 'header' }], IHttpHeaderParam> ->(function (param) { - const name = param.name; - +export const translateToHeaderParam: Oas2TranslateFunction< + [param: DeepPartial & Oas2ParamBase & { in: 'header' }], + IHttpHeaderParam +> = function (parameter) { return { - id: this.generateId(`http_header-${this.parentId}-${name}`), - name, style: HttpParamStyles.Simple, - ...buildSchemaForParameter.call(this, param), - required: !!param.required, + name: parameter.name, + ...buildSchemaForParameter.call(this, parameter), + required: !!parameter.required, }; -}); +}; const translateToHeaderParamsFromPair: Oas2TranslateFunction< ArrayCallbackParameters<[name: string, value: unknown]>, @@ -94,54 +89,39 @@ export const translateToHeaderParams: Oas2TranslateFunction<[headers: unknown], return entries(headers).map(translateToHeaderParamsFromPair, this).filter(isNonNullable); }; -export const translateToBodyParameter = withContext< - Oas2TranslateFunction<[body: BodyParameter, consumes: string[]], IHttpOperationRequestBody> ->(function (body, consumes) { - const examples = entries(body['x-examples'] || getExamplesFromSchema(body.schema)).map(([key, value]) => - translateToDefaultExample.call(this, key, value), +export const translateToBodyParameter: Oas2TranslateFunction< + [body: BodyParameter, consumes: string[]], + IHttpOperationRequestBody +> = function (body, consumes) { + const examples = entries(body['x-examples'] || (body.schema ? getExamplesFromSchema(body.schema) : void 0)).map( + ([key, value]) => ({ key, value }), ); - return { - id: this.generateId(`http_request_body-${this.parentId}`), - - required: !!body.required, - contents: consumes.map( - withContext(mediaType => { - return { - id: this.generateId(`http_media-${this.parentId}-${mediaType}`), - mediaType, - schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, - examples, - }; - }), - this, - ), - - ...pickBy( - { - description: body.description, - }, - isString, - ), - }; -}); + return pickBy({ + description: body.description, + required: body.required, + contents: consumes.map(mediaType => { + return { + mediaType, + schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, + examples, + }; + }), + }); +}; -export const translateFromFormDataParameters = withContext< - Oas2TranslateFunction< - [parameters: (Oas2ParamBase & Partial)[], consumes: string[]], - IHttpOperationRequestBody - > ->(function (parameters, consumes) { +export const translateFromFormDataParameters: Oas2TranslateFunction< + [parameters: FormDataParameter[], consumes: string[]], + IHttpOperationRequestBody +> = function (parameters, consumes) { const finalBody: IHttpOperationRequestBody = { - id: this.generateId(`http_request_body-${this.parentId}`), - contents: consumes.map( - withContext(mediaType => ({ - id: this.generateId(`http_media-${this.parentId}-${mediaType}`), - mediaType, - schema: translateSchemaObject.call(this, { type: 'object' }), - })), - this, - ), + contents: consumes.map(mediaType => ({ + mediaType, + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + }, + })), }; return parameters.reduce((body, parameter) => { @@ -171,9 +151,9 @@ export const translateFromFormDataParameters = withContext< }); return body; }, finalBody); -}); +}; -function buildEncoding(parameter: Oas2ParamBase & Partial): IHttpEncoding | null { +function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { switch (parameter.collectionFormat) { case 'csv': return { @@ -203,41 +183,36 @@ function buildEncoding(parameter: Oas2ParamBase & Partial): I return null; } -export const translateToQueryParameter = withContext< - Oas2TranslateFunction<[query: DeepPartial & Oas2ParamBase], IHttpQueryParam> ->(function (param) { - const name = param.name; - +export const translateToQueryParameter: Oas2TranslateFunction< + [query: DeepPartial & Oas2ParamBase], + IHttpQueryParam +> = function (query) { return { - id: this.generateId(`http_query-${this.parentId}-${name}`), - name, - style: chooseQueryParameterStyle(param), - - required: !!param.required, - ...buildSchemaForParameter.call(this, param), + style: chooseQueryParameterStyle(query), + name: query.name, + required: !!query.required, + ...buildSchemaForParameter.call(this, query), ...pickBy( { - allowEmptyValue: param.allowEmptyValue, + allowEmptyValue: query.allowEmptyValue, }, isBoolean, ), }; -}); - -export const translateToPathParameter = withContext< - Oas2TranslateFunction<[param: DeepPartial & Oas2ParamBase], IHttpPathParam> ->(function (param) { - const name = param.name; +}; +export const translateToPathParameter: Oas2TranslateFunction< + [param: DeepPartial & Oas2ParamBase], + IHttpPathParam +> = function (param) { return { - id: this.generateId(`http_path_param-${this.parentId}-${name}`), - name, + name: param.name, style: HttpParamStyles.Simple, required: !!param.required, ...buildSchemaForParameter.call(this, param), }; -}); +}; const buildSchemaForParameter: Oas2TranslateFunction< [param: DeepPartial], diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index d75b616e..cdf894e3 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,10 +1,12 @@ import type { IHttpOperationRequest } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import type { BodyParameter, FormDataParameter } from 'swagger-schema-official'; import { isNonNullable } from '../../guards'; import { OasVersion } from '../../oas'; -import { createOasParamsIterator } from '../../oas/accessors'; +import { getValidOasParameters } from '../../oas/accessors'; import { getConsumes } from '../accessors'; -import { isBodyParam, isFormDataParam, isHeaderParam, isPathParam, isQueryParam } from '../guards'; +import { isHeaderParam, isPathParam, isQueryParam } from '../guards'; import { Oas2TranslateFunction } from '../types'; import { translateFromFormDataParameters, @@ -13,17 +15,16 @@ import { translateToPathParameter, translateToQueryParameter, } from './params'; -import pickBy = require('lodash.pickby'); -import { Oas2ParamBase } from '../../oas/guards'; - -const iterateOasParams = createOasParamsIterator(OasVersion.OAS2); export const translateToRequest: Oas2TranslateFunction< [path: Record, operation: Record], IHttpOperationRequest > = function (path, operation) { const consumes = getConsumes(this.document, operation); - const parameters = iterateOasParams.call(this, operation, path); + const parameters = getValidOasParameters(this.document, OasVersion.OAS2, operation.parameters, path.parameters); + + const bodyParameter = parameters.find((p): p is BodyParameter => p.in === 'body'); + const formDataParameters = parameters.filter((p): p is FormDataParameter => p.in === 'formData'); const params: Omit, 'body'> = { headers: [], @@ -32,8 +33,14 @@ export const translateToRequest: Oas2TranslateFunction< path: [], }; - let bodyParameter; - const formDataParameters: (Oas2ParamBase & { in: 'formData' })[] = []; + let body; + // if 'body' and 'form data' defined prefer 'body' + if (!!bodyParameter) { + // There can be only one body parameter (taking first one) + body = translateToBodyParameter.call(this, bodyParameter, consumes); + } else if (!!formDataParameters.length) { + body = translateFromFormDataParameters.call(this, formDataParameters, consumes); + } for (const param of parameters) { if (isQueryParam(param)) { @@ -42,22 +49,9 @@ export const translateToRequest: Oas2TranslateFunction< params.path.push(translateToPathParameter.call(this, param)); } else if (isHeaderParam(param)) { params.headers.push(translateToHeaderParam.call(this, param)); - } else if (isBodyParam(param)) { - bodyParameter = translateToBodyParameter.call(this, param, consumes); - } else if (isFormDataParam(param)) { - formDataParameters.push(param); } } - let body; - // if 'body' and 'form data' defined prefer 'body' - if (!!bodyParameter) { - // There can be only one body parameter (taking first one) - body = bodyParameter; - } else if (!!formDataParameters.length) { - body = translateFromFormDataParameters.call(this, formDataParameters, consumes); - } - return { ...params, diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index c41ed3a6..34b58bd8 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,12 +1,9 @@ import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial, IHttpOperationResponse, IMediaTypeContent, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { Operation } from 'swagger-schema-official'; +import type { IHttpOperationResponse, Optional } from '@stoplight/types'; +import { DeepPartial } from '@stoplight/types'; +import { Operation } from 'swagger-schema-official'; -import { withContext } from '../../context'; import { isNonNullable } from '../../guards'; -import { getSharedKey } from '../../oas/resolver'; -import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { entries } from '../../utils'; import { getExamplesFromSchema, getProduces } from '../accessors'; @@ -14,40 +11,29 @@ import { isResponseObject } from '../guards'; import { Oas2TranslateFunction } from '../types'; import { translateToHeaderParams } from './params'; -const translateToResponse = withContext< - Oas2TranslateFunction<[produces: string[], statusCode: string, response: unknown], Optional> ->(function (produces, statusCode, response) { +const translateToResponse: Oas2TranslateFunction< + [produces: string[], statusCode: string, response: unknown], + Optional +> = function (produces, statusCode, response) { const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; - const actualKey = this.context === 'service' ? getSharedKey(resolvedResponse) : statusCode; - const headers = translateToHeaderParams.call(this, resolvedResponse.headers); - const objectifiedExamples = entries(resolvedResponse.examples || getExamplesFromSchema(resolvedResponse.schema)).map( - ([key, value]) => translateToDefaultExample.call(this, key, value), - ); + const objectifiedExamples = entries( + resolvedResponse.examples || (resolvedResponse.schema ? getExamplesFromSchema(resolvedResponse.schema) : void 0), + ).map(([key, value]) => ({ key, value })); const contents = produces - .map }>( - withContext(produceElement => ({ - id: this.generateId(`http_media-${this.parentId}-${produceElement}`), - mediaType: produceElement, - examples: objectifiedExamples.filter(example => example.key === produceElement), - ...pickBy( - { - schema: isPlainObject(resolvedResponse.schema) - ? translateSchemaObject.call(this, resolvedResponse.schema) - : undefined, - }, - isNonNullable, - ), - })), - this, - ) + .map(produceElement => ({ + mediaType: produceElement, + schema: isPlainObject(resolvedResponse.schema) + ? translateSchemaObject.call(this, resolvedResponse.schema) + : void 0, + examples: objectifiedExamples.filter(example => example.key === produceElement), + })) .filter(({ schema, examples }) => !!schema || examples.length > 0); const translatedResponses = { - id: this.generateId(`http_response-${this.parentId}-${actualKey}`), code: statusCode, description: resolvedResponse.description, headers, @@ -58,7 +44,6 @@ const translateToResponse = withContext< if (foreignExamples.length > 0) { if (translatedResponses.contents.length === 0) translatedResponses.contents[0] = { - id: this.generateId(`http_media-${this.parentId}-`), mediaType: '', schema: {}, examples: [], @@ -68,7 +53,7 @@ const translateToResponse = withContext< } return translatedResponses; -}); +}; export const translateToResponses: Oas2TranslateFunction< [operation: DeepPartial], diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index ed674d04..c7123286 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -1,7 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, - HttpSecurityScheme, IApiKeySecurityScheme, IBasicSecurityScheme, IOauth2SecurityScheme, @@ -18,11 +17,11 @@ import type { OAuth2PasswordSecurity, } from 'swagger-schema-official'; -import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; +import { SecurityWithKey } from '../../oas3/accessors'; import { getSecurities } from '../accessors'; import { isSecurityScheme } from '../guards'; -import type { Oas2TranslateFunction } from '../types'; +import { Oas2TranslateFunction } from '../types'; export const translateToFlows: Oas2TranslateFunction<[security: Record], IOauthFlowObjects> = function (security) { @@ -59,86 +58,60 @@ export const translateToFlows: Oas2TranslateFunction<[security: Record & { key: string }], IBasicSecurityScheme> ->(function (security) { - const key = security.key; - +export const translateToBasicSecurityScheme: Oas2TranslateFunction< + [security: DeepPartial & { key: string }], + IBasicSecurityScheme +> = function (security) { return { - id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'http', scheme: 'basic', - key, - - ...pickBy( - { - description: security.description, - }, - isString, - ), + description: security.description, + key: security.key, }; -}); +}; const ACCEPTABLE_SECURITY_ORIGINS: ApiKeySecurity['in'][] = ['query', 'header']; -export const translateToApiKeySecurityScheme = withContext< - Oas2TranslateFunction<[security: DeepPartial & { key: string }], Optional> ->(function (security) { +export const translateToApiKeySecurityScheme: Oas2TranslateFunction< + [security: DeepPartial & { key: string }], + Optional +> = function (security) { if ('in' in security && security.in && ACCEPTABLE_SECURITY_ORIGINS.includes(security.in)) { - const key = security.key; - return { - id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'apiKey', in: security.in, - name: isString(security.name) ? security.name : '', - key, - - ...pickBy( - { - description: security.description, - }, - isString, - ), + name: String(security.name || ''), + description: security.description, + key: security.key, }; } return; -}); +}; const VALID_OAUTH2_FLOWS = ['implicit', 'password', 'application', 'accessCode']; -export const translateToOauth2SecurityScheme = withContext< - Oas2TranslateFunction< - [ - security: DeepPartial< - OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity - > & { key: string }, - ], - Optional - > ->(function (security) { +export const translateToOauth2SecurityScheme: Oas2TranslateFunction< + [ + security: DeepPartial< + OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity + > & { key: string }, + ], + Optional +> = function (security) { if (!security.flow || !VALID_OAUTH2_FLOWS.includes(security.flow)) return undefined; - const key = security.key; return { - id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'oauth2', flows: translateToFlows.call(this, security), - key, - - ...pickBy( - { - description: security.description, - }, - isString, - ), + description: security.description, + key: security.key, }; -}); +}; export const translateToSingleSecurity: Oas2TranslateFunction< [security: unknown & { key: string }], - Optional + Optional > = function (security) { if (isSecurityScheme(security)) { switch (security.type) { @@ -154,7 +127,7 @@ export const translateToSingleSecurity: Oas2TranslateFunction< return; }; -export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], HttpSecurityScheme[][]> = +export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = function (operationSecurities) { const securities = getSecurities(this.document, operationSecurities); diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index af564d5a..7be41c5d 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,33 +1,26 @@ import type { IServer, Optional } from '@stoplight/types'; - -import { withContext } from '../../context'; import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../../guards'; -import { ArrayCallbackParameters } from '../../types'; import { isValidScheme } from '../guards'; import type { Oas2TranslateFunction } from '../types'; -export const translateToServers = withContext], IServer[]>>( - function (operation) { - let schemes; - if (Array.isArray(operation.schemes)) { - schemes = operation.schemes; - this.context = 'operation'; - } else if (Array.isArray(this.document.schemes)) { - schemes = this.document.schemes; - this.context = 'service'; - } else { - return []; - } - - return schemes.map(translateToServer, this).filter(isNonNullable); - }, -); - -export const translateToServer = withContext< - Oas2TranslateFunction, Optional> ->(function (scheme) { +export const translateToServers: Oas2TranslateFunction<[operation: Record], IServer[]> = function ( + operation, +) { + let schemes; + if (Array.isArray(operation.schemes)) { + schemes = operation.schemes; + } else if (Array.isArray(this.document.schemes)) { + schemes = this.document.schemes; + } else { + return []; + } + + return schemes.map(translateToServer, this).filter(isNonNullable); +}; + +export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optional> = function (scheme) { const { host } = this.document; if (typeof host !== 'string' || host.length === 0) { return; @@ -46,11 +39,8 @@ export const translateToServer = withContext< uri.pathname = basePath; } - const url = uri.toString().replace(/\/$/, ''); // Remove trailing slash - return { - id: this.generateId(`http_server-${this.parentId}-${url}`), - url, + url: uri.toString().replace(/\/$/, ''), // Remove trailing slash ...pickBy( { @@ -59,4 +49,4 @@ export const translateToServer = withContext< isString, ), }; -}); +}; diff --git a/src/oas3/__fixtures__/id.json b/src/oas3/__fixtures__/id.json deleted file mode 100644 index cd66100f..00000000 --- a/src/oas3/__fixtures__/id.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "openapi": "3.1.0", - "x-stoplight-id": "service_abc", - "info": { - "title": "Users API", - "version": "1.0" - }, - "servers": [ - { - "url": "http://localhost:3000" - } - ], - "tags": [ - { - "name": "mutates" - } - ], - "paths": { - "/users/{userId}": { - "parameters": [ - { - "schema": { - "type": "integer" - }, - "name": "userId", - "in": "path", - "required": true, - "description": "Id of an existing user." - }, - { - "$ref": "#/components/parameters/Some-Header" - } - ], - "get": { - "operationId": "get-user", - "summary": "Get User Info by User ID", - "description": "Retrieve the information of the user with the matching user ID.", - "tags": ["tag-without-root-def"], - "parameters": [ - { - "schema": { - "type": "boolean" - }, - "in": "query", - "name": "summaryOnly" - } - ], - "responses": { - "200": { - "description": "User Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - }, - "application/xml": { - "schema": { - "type": "string" - } - } - } - }, - "404": { - "$ref": "#/components/responses/ErrorResponse" - } - } - }, - "post": { - "operationId": "post-users-userId", - "summary": "Create user", - "tags": ["mutates"], - "parameters": [ - { - "schema": { - "type": "integer" - }, - "name": "Post-Specific-Header", - "in": "header" - } - ], - "security": [ - { - "api-key": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - }, - "examples": { - "basic-example": { - "$ref": "#/components/examples/A-Shared-Example" - } - } - } - } - }, - "responses": { - "201": { - "description": "User Created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "$ref": "#/components/responses/ErrorResponse" - } - } - } - } - }, - "components": { - "schemas": { - "User": { - "title": "User", - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "address": { - "$ref": "#/components/schemas/Address" - } - }, - "required": ["id"], - "examples": [] - }, - "Address": { - "title": "Address", - "type": "object", - "properties": { - "street": { - "type": "string" - } - }, - "examples": [ - { - "street": "422 W Riverside Drive" - } - ] - }, - "UserId": { - "type": "number", - "title": "UserId", - "minimum": 0 - }, - "Error": { - "title": "Error", - "type": "object", - "properties": { - "code": { - "type": "number" - }, - "msg": { - "type": "string" - } - }, - "required": ["code", "msg"] - } - }, - "parameters": { - "Some-Header": { - "name": "A-Shared-Header", - "in": "header", - "required": false, - "schema": { - "type": "string" - } - } - }, - "examples": { - "A-Shared-Example": { - "value": { - "id": 0, - "address": { - "street": "string" - } - } - } - }, - "securitySchemes": { - "api-key": { - "name": "API Key", - "type": "apiKey", - "in": "query" - } - }, - "responses": { - "ErrorResponse": { - "description": "A generic error response.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - } - } -} diff --git a/src/oas3/__fixtures__/output.ts b/src/oas3/__fixtures__/output.ts deleted file mode 100644 index 54bd1fc8..00000000 --- a/src/oas3/__fixtures__/output.ts +++ /dev/null @@ -1,489 +0,0 @@ -/** - NOTE that if any object anywhere ever has an `x-stoplight-id` on it, prefer that - over calling the generate function. - - Used https://md5calc.com/hash/fnv1a32 to hash the ids. - */ -export default [ - /** - * The http_service - */ - { - // hash(document id - end user needs to be able to customize this..) - // by default could hash("#")? - // this example has a x-stoplight-id prop on the root though, so using that - id: 'service_abc', - version: '1.0', - name: 'Users API', - servers: [ - { - // hash(`http_server-${parentId}-${server.url}`) - // closest parent with an id is the service, so ends up being... - // hash('http_server-service_abc-http://localhost:3000') - id: '98ddf8a4b5bdc', - name: 'Users API', - url: 'http://localhost:3000', - }, - ], - securitySchemes: [ - { - // hash(`http_security-${parentId}-${security.key || security.name}`) - // closest parent with an id is the service, so ends up being... - // hash('http_security-service_abc-api-key') - id: '202a905f9dff6', - key: 'api-key', - type: 'apiKey', - name: 'API Key', - in: 'query', - }, - ], - tags: [ - { - // hash(`tag-${serviceId}-${tag.name}`) - // always generate tags based on the serviceId, so ends up being... - // hash('tag-service_abc-mutates') - id: '936737e88c6fa', - name: 'mutates', - }, - ], - }, - - /** - * http_operation 1 of 2 (the GET operation) - */ - { - // hash(`http_operation-${parentId}-${method}-${pathWithParamNamesEmpty}`) - // for pathWithParamNamesEmpty, remove all characters between {} segments - // closest parent with an id is the service, so ends up being... - // hash('http_operation-service_abc-get-/users/{}') - id: '96043a63b6901', - iid: 'get-user', - description: 'Retrieve the information of the user with the matching user ID.', - method: 'get', - path: '/users/{userId}', - summary: 'Get User Info by User ID', - deprecated: false, - internal: false, - responses: [ - { - // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) - // closest parent with an id is the operation, so ends up being... - // hash('http_response-96043a63b6901-200') - id: 'f387e16c7d39d', - code: '200', - description: 'User Found', - headers: [], - contents: [ - { - // hash(`http_media-${parentId}-${mediaType}`) - // closest parent with an id is the response, so ends up being... - // hash('http_media-f387e16c7d39d-application/json') - id: 'fce50f391bf57', - mediaType: 'application/json', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': 'de4f083463b7c', - title: 'User', - type: 'object', - properties: { - id: { - type: 'integer', - readOnly: true, - }, - address: { - // @TODO - $ref: '#/components/schemas/Address', - }, - }, - required: ['id'], - examples: [], - }, - examples: [], - encodings: [], - }, - { - // hash(`http_media-${parentId}-${mediaType}`) - // closest parent with an id is the response, so ends up being... - // hash('http_media-f387e16c7d39d-application/xml') - id: '48eeb3ee2a049', - mediaType: 'application/xml', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': '069dfbb6c6315', - type: 'string', - }, - examples: [], - encodings: [], - }, - ], - }, - { - // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) - // This response was defined in shared components originally.. the closest parent with an - // id is the service, and the key was ErrorResponse so ends up being... - // hash('http_response-service_abc-ErrorResponse') - id: '437771f63f179', - code: '404', - description: 'A generic error response.', - headers: [], - contents: [ - { - // hash(`http_media-${parentId}-${mediaType}`) - // closest parent with an id is the response, so ends up being... - // hash('http_media-437771f63f179-application/json') - id: '4d98be34f341a', - mediaType: 'application/json', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': '2691eb0db9efc', - type: 'object', - properties: { - error: { - // @TODO - $ref: '#/components/schemas/Error', - }, - }, - }, - examples: [], - encodings: [], - }, - ], - }, - ], - servers: [ - { - // hash(`http_server-${parentId}-${server.url}`) - // this is coming from the service defined servers (rather than being defined specifically for this one operation) - // so the ID ends up being the same as the service defined one... look in file for "98ddf8a4b5bdc" to find the other def above - id: '98ddf8a4b5bdc', - name: 'Users API', - url: 'http://localhost:3000', - }, - ], - request: { - // Request doesn't need an id - body: { - id: 'd5027559477f8', - // Really this doesn't even need to be here... there is no request body for this op - contents: [], - }, - headers: [ - { - // hash(`http_header-${parentId}-${param.name}`) - // This header was defined in shared components originally, note how this ends up appearing several times in this doc. - // The closest parent with an id is the service, so ends up being... - // hash('http_header-service_abc-A-Shared-Header') - id: '21b1f96bd26ee', - name: 'A-Shared-Header', - required: false, - deprecated: false, - explode: false, - style: 'simple', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': 'be6b513de1b69', - type: 'string', - }, - examples: [], - }, - ], - query: [ - { - // hash(`http_query-${parentId}-${param.name}`) - // This was defined directly on the operation (not a shared component), so the closest - // parent with an id is the operation, so ends up being... - // hash('http_query-96043a63b6901-summaryOnly') - id: 'efe9534d001fc', - name: 'summaryOnly', - required: false, - deprecated: false, - explode: false, - style: 'simple', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'boolean', - 'x-stoplight-id': 'aca62504578bd', - }, - examples: [], - }, - ], - cookie: [], - path: [ - { - // hash(`http_path_param-${parentId}-${param.name}`) - // This was defined on the path, so we use the path to generate the id (thus if another operation on this path was in this doc, it would have path param with same id) - // path's id = hash(`http_path-${parentId}-${path}`) - // The closest parent id to a path, is the service, so this equals... (remember that path segments have characters removed, since they are basically meaningless) - // hash('http_path-service_abc-/users/{}') = '05574f79' - // and then the final path param id... - // hash('http_path_param-05574f79-userId') = 'fe171ec8cfd0b' - id: 'fe171ec8cfd0b', - name: 'userId', - required: true, - description: 'Id of an existing user.', - explode: false, - deprecated: false, - style: 'simple', - schema: { - 'x-stoplight-id': '13ad531bed72e', - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'integer', - }, - examples: [], - }, - ], - }, - tags: [ - { - // hash(`tag-${serviceId}-${tag.name}`) - // always generate tags based on the serviceId, so ends up being... - // hash('tag-service_abc-tag-without-root-def') - id: '9862017e672e6', - name: 'tag-without-root-def', - }, - ], - security: [], - extensions: {}, - }, - - /** - * http_operation 2 of 2 (the POST operation) - */ - { - // Same process as first time... and yes, I know "POST" doesn't make sense on this path lol - id: 'b16a96d287951', - iid: 'post-users-userId', - method: 'post', - path: '/users/{userId}', - summary: 'Create user', - deprecated: false, - internal: false, - responses: [ - { - // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) - // closest parent with an id is the operation, so ends up being... - // hash('http_response-b16a96d287951-201') - id: 'd8ca38606ee5d', - code: '201', - description: 'User Created', - headers: [], - contents: [ - { - // hash(`http_media-${parentId}-${mediaType}`) - // closest parent with an id is the response, so ends up being... - // hash('http_media-d8ca38606ee5d-application/json') - id: '88460a8f1a612', - mediaType: 'application/json', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': 'de4f083463b7c', - title: 'User', - type: 'object', - properties: { - id: { - type: 'integer', - readOnly: true, - }, - address: { - // @TODO - $ref: '#/components/schemas/Address', - }, - }, - required: ['id'], - examples: [], - }, - examples: [], - encodings: [], - }, - ], - }, - { - // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) - // This response was defined in shared components originally.. the closest parent with an - // id is the service, and the key was ErrorResponse so ends up being... - // hash('http_response-service_abc-ErrorResponse') - // NOTE how this ID is the same as the 404 response from the get user operation... - id: '437771f63f179', - code: '400', - description: 'A generic error response.', - headers: [], - contents: [ - { - // hash(`http_media-${parentId}-${mediaType}`) - // closest parent with an id is the response, so ends up being... - // hash('http_media-437771f63f179-application/json') - // NOTE how this ID is the same as the 404 response json media type from the get user operation... - id: '4d98be34f341a', - mediaType: 'application/json', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - // hash(`schema-${parentId}-${key}`) - // This schema was defined in shared components originally.. the closest parent with an - // id is the service, and the key was ErrorResponse so ends up being... - // hash('schema-4d98be34f341a-ErrorResponse') - 'x-stoplight-id': '2691eb0db9efc', - type: 'object', - properties: { - error: { - // @TODO - $ref: '#/components/schemas/Error', - }, - }, - }, - examples: [], - encodings: [], - }, - ], - }, - ], - servers: [ - { - // hash(`http_server-${parentId}-${server.url}`) - // this is coming from the service defined servers (rather than being defined specifically for this one operation) - // so the ID ends up being the same as the service defined one (and also present in the get user op)... - // look in file for "98ddf8a4b5bdc" to find the other defs above - id: '98ddf8a4b5bdc', - name: 'Users API', - url: 'http://localhost:3000', - }, - ], - request: { - body: { - // hash(`http_request_body-${parentId}`) - // closest parent with an id is the operation, so ends up being... - // hash('http_request_body-b16a96d287951') - id: '07267ec331fc9', - contents: [ - { - // hash(`http_media-${parentId}-${mediaType}`) - // closest parent with an id is the request body, so ends up being... - // hash('http_media-07267ec331fc9-application/json') - id: '00db77c676e1a', - mediaType: 'application/json', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': 'de4f083463b7c', - title: 'User', - type: 'object', - properties: { - id: { - type: 'integer', - readOnly: true, - }, - address: { - // @TODO - $ref: '#/components/schemas/Address', - }, - }, - required: ['id'], - examples: [], - }, - examples: [ - { - // hash(`example-${parentId}-${example key}`) - // This example was defined in shared components originally.. the closest parent with an - // id is the service, and the key was "A-Shared-Example" so ends up being... - // hash('example-service_abc-A-Shared-Example') - id: '5a69041e065b0', - key: 'basic-example', - value: { - id: 0, - address: { - street: 'string', - }, - }, - }, - ], - encodings: [], - }, - ], - }, - headers: [ - { - // hash(`http_header-${parentId}-${param.name}`) - // This was defined directly on the operation (not a shared component), so the closest - // parent with an id is the operation, so ends up being... - // hash('http_header-b16a96d287951-Post-Specific-Header') - id: '1ead595922478', - name: 'Post-Specific-Header', - required: false, - deprecated: false, - explode: false, - style: 'simple', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': 'de5897f178a5d', - type: 'integer', - }, - examples: [], - }, - { - // hash(`http_header-${parentId}-${param.name}`) - // This header was defined in shared components originally, note how this ends up appearing several times in this doc. - // The closest parent with an id is the service, so ends up being... - // hash('http_header-service_abc-A-Shared-Header') - id: '21b1f96bd26ee', - name: 'A-Shared-Header', - required: false, - deprecated: false, - explode: false, - style: 'simple', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - 'x-stoplight-id': 'be6b513de1b69', - type: 'string', - }, - examples: [], - }, - ], - query: [], - cookie: [], - path: [ - { - // Same process as other path param, resulting in the same - // ID (so this path param node will end up as single instance in the graph, with an edge from each operation pointing at it) - id: 'fe171ec8cfd0b', - name: 'userId', - required: true, - description: 'Id of an existing user.', - deprecated: false, - explode: false, - style: 'simple', - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - // hash(`http_media-${parentId}-${key}`) - // closest parent with an id is the response, so ends up being... - // hash('http_media-437771f63f179-application/json') - 'x-stoplight-id': '13ad531bed72e', - type: 'integer', - }, - examples: [], - }, - ], - }, - tags: [ - { - // hash(`tag-${serviceId}-${tag.name}`) - // always generate tags based on the serviceId, so ends up being... - // hash('tag-service_abc-mutates') - id: '936737e88c6fa', - name: 'mutates', - }, - ], - security: [ - [ - { - // This is effectively a silly "fake" ref that openapi pulls... so - // we can effectively just re-use the same ID for the relevant securityScheme - // from the root.. note the ID is the same as the root securityScheme id - id: '202a905f9dff6', - key: 'api-key', - type: 'apiKey', - name: 'API Key', - in: 'query', - }, - ], - ], - extensions: {}, - }, -]; diff --git a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap index bd8c38a7..92aa3bb3 100644 --- a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap @@ -5,17 +5,14 @@ Object { "callbacks": Array [ Object { "callbackName": "myCallback", - "deprecated": false, "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "cbId", - "internal": false, "method": "post", "path": "http://example.com?transactionId={$request.body#/id}", "request": Object { "body": Object { "contents": Array [], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -28,11 +25,9 @@ Object { "tags": Array [], }, ], - "deprecated": false, "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "opid", - "internal": false, "method": "post", "path": "/subscribe", "request": Object { @@ -41,7 +36,6 @@ Object { Object { "encodings": Array [], "examples": Array [], - "id": Any, "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -51,11 +45,9 @@ Object { }, }, "type": "object", - "x-stoplight-id": Any, }, }, ], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -74,15 +66,13 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "opid", - "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -102,15 +92,13 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "opid", - "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -121,7 +109,6 @@ Object { "security": Array [], "servers": Array [ Object { - "id": Any, "name": "title", "url": "operation/server", }, @@ -136,15 +123,13 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "opid", - "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -155,7 +140,6 @@ Object { "security": Array [], "servers": Array [ Object { - "id": Any, "name": "title", "url": "path/server", }, @@ -170,15 +154,13 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "opid", - "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -191,7 +173,6 @@ Object { "summary": "summary", "tags": Array [ Object { - "id": Any, "name": "tag1", }, ], @@ -203,15 +184,13 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": Any, + "id": "?http-operation-id?", "iid": "opid", - "internal": false, "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], - "id": Any, }, "cookie": Array [], "headers": Array [], @@ -222,7 +201,6 @@ Object { "security": Array [], "servers": Array [ Object { - "id": Any, "name": "title", "url": "spec/server", }, diff --git a/src/oas3/__tests__/accessors.test.ts b/src/oas3/__tests__/accessors.test.ts index 4b0e7249..5ccb31d5 100644 --- a/src/oas3/__tests__/accessors.test.ts +++ b/src/oas3/__tests__/accessors.test.ts @@ -1,10 +1,4 @@ -import { DeepPartial } from '@stoplight/types'; -import { OpenAPIObject } from 'openapi3-ts'; - -import { getSecurities as _getSecurities, OperationSecurities } from '../accessors'; - -const getSecurities = (document: DeepPartial, operationSecurities?: OperationSecurities) => - _getSecurities(document, operationSecurities); +import { getSecurities } from '../accessors'; describe('getOas3Securities', () => { it('given no global securities should return empty array', () => { @@ -39,12 +33,10 @@ describe('getOas3Securities', () => { }), ).toStrictEqual([ [ - [ - 'operationScheme', - { - type: 'apiKey', - }, - ], + { + type: 'apiKey', + key: 'operationScheme', + }, ], ]); }); @@ -65,12 +57,10 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([ [ - [ - 'specScheme', - { - type: 'apiKey', - }, - ], + { + type: 'apiKey', + key: 'specScheme', + }, ], ]); }); @@ -95,12 +85,10 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([ [ - [ - 'specScheme', - { - type: 'apiKey', - }, - ], + { + type: 'apiKey', + key: 'specScheme', + }, ], ]); }); @@ -125,4 +113,62 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([[]]); }); + + it('should return security for each scope', () => { + const res = getSecurities( + { + components: { + securitySchemes: { + authWith2Scopes: { + type: 'oauth2', + flows: { + authorizationCode: { + scopes: { + accessToken: 'accessToken description', + secScope: 'secScope description', + }, + }, + }, + }, + }, + }, + security: [ + { + authWith2Scopes: ['accessToken', 'secScope'], + }, + ], + }, + [ + { + authWith2Scopes: ['accessToken'], + }, + { authWith2Scopes: ['secScope'] }, + ], + ); + + expect(res).toStrictEqual([ + [ + { + type: 'oauth2', + flows: { + authorizationCode: { + scopes: { accessToken: 'accessToken description' }, + }, + }, + key: 'authWith2Scopes', + }, + ], + [ + { + type: 'oauth2', + flows: { + authorizationCode: { + scopes: { secScope: 'secScope description' }, + }, + }, + key: 'authWith2Scopes', + }, + ], + ]); + }); }); diff --git a/src/oas3/__tests__/ids.test.ts b/src/oas3/__tests__/ids.test.ts deleted file mode 100644 index 7aa3126b..00000000 --- a/src/oas3/__tests__/ids.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; - -import { transformOas3Operations } from '../operation'; -import { transformOas3Service } from '../service'; - -test('should generate proper ids', async () => { - const document = JSON.parse(await fs.promises.readFile(path.join(__dirname, '../__fixtures__/id.json'), 'utf8')); - const { default: output } = await import('../__fixtures__/output'); - - expect([transformOas3Service({ document }), ...transformOas3Operations(document)]).toEqual(output); -}); diff --git a/src/oas3/__tests__/operation.test.ts b/src/oas3/__tests__/operation.test.ts index 56a11075..fdba8752 100644 --- a/src/oas3/__tests__/operation.test.ts +++ b/src/oas3/__tests__/operation.test.ts @@ -1,13 +1,6 @@ import { OpenAPIObject } from 'openapi3-ts'; -import { - transformOas3Operation as _transformOas3Operation, - transformOas3Operations as _transformOas3Operations, -} from '../operation'; - -const transformOas3Operation: typeof _transformOas3Operation = opts => _transformOas3Operation(opts); - -const transformOas3Operations: typeof _transformOas3Operations = opts => _transformOas3Operations(opts); +import { transformOas3Operation, transformOas3Operations } from '../operation'; describe('transformOas3Operation', () => { it('should return deprecated property in http operation root', () => { @@ -92,7 +85,7 @@ describe('transformOas3Operation', () => { }, }; - expect(transformOas3Operations(document)).toEqual([ + expect(transformOas3Operations(document)).toStrictEqual([ expect.objectContaining({ path: '/users/{userId}', method: 'get', @@ -105,7 +98,8 @@ describe('transformOas3Operation', () => { internal: false, extensions: {}, }), - expect.objectContaining({ + { + id: '?http-operation-id?', path: '/users/{userId}', method: 'put', request: expect.any(Object), @@ -114,7 +108,7 @@ describe('transformOas3Operation', () => { servers: expect.any(Array), tags: [], extensions: {}, - }), + }, ]); }); @@ -144,14 +138,7 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - request: { - body: { - id: expect.any(String), - }, - }, - }); + ).toMatchSnapshot(); }); it('given some tags should translate operation with those tags', () => { @@ -187,19 +174,7 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - request: { - body: { - id: expect.any(String), - }, - }, - tags: [ - { - id: expect.any(String), - }, - ], - }); + ).toMatchSnapshot(); }); it('given invalid tags should translate operation as there were no tags specified', () => { @@ -261,15 +236,12 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('tags', [ { - id: expect.any(String), name: '2', }, { - id: expect.any(String), name: 'test', }, { - id: expect.any(String), name: 'false', }, ]); @@ -302,19 +274,7 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - request: { - body: { - id: expect.any(String), - }, - }, - servers: [ - { - id: expect.any(String), - }, - ], - }); + ).toMatchSnapshot(); }); it.each([2, '', null, [null]])( @@ -379,7 +339,6 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - id: expect.any(String), name: 'title', url: 'operation/server', }, @@ -413,19 +372,7 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - request: { - body: { - id: expect.any(String), - }, - }, - servers: [ - { - id: expect.any(String), - }, - ], - }); + ).toMatchSnapshot(); }); it.each([2, '', null, [null]])( @@ -490,7 +437,6 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - id: expect.any(String), name: 'title', url: 'path/server', }, @@ -524,19 +470,7 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - request: { - body: { - id: expect.any(String), - }, - }, - servers: [ - { - id: expect.any(String), - }, - ], - }); + ).toMatchSnapshot(); }); it.each([2, '', null, [null]])( @@ -601,7 +535,6 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - id: expect.any(String), name: 'title', url: 'spec/server', }, @@ -655,32 +588,7 @@ describe('transformOas3Operation', () => { method: 'post', document, }), - ).toMatchSnapshot({ - id: expect.any(String), - request: { - body: { - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - ], - }, - }, - callbacks: [ - { - id: expect.any(String), - request: { - body: { - id: expect.any(String), - }, - }, - }, - ], - }); + ).toMatchSnapshot(); }); it('given malformed parameters should translate operation with those parameters', () => { @@ -717,23 +625,18 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: expect.any(String), + id: '?http-operation-id?', method: 'get', path: '/users/{userId}', - deprecated: false, - internal: false, request: { body: { - id: expect.any(String), contents: [], }, cookie: [], headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'test', }, @@ -741,10 +644,8 @@ describe('transformOas3Operation', () => { name: 'name', deprecated: false, explode: false, - required: false, style: 'simple', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', format: 'int32', @@ -804,34 +705,27 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: expect.any(String), + id: '?http-operation-id?', method: 'get', path: '/users/{userId}', - deprecated: false, - internal: false, request: { body: { - id: expect.any(String), contents: [], }, cookie: [], headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'some example', }, ], - required: false, deprecated: false, explode: false, style: 'simple', name: 'name', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', format: 'int32', @@ -854,6 +748,7 @@ describe('transformOas3Operation', () => { it('should keep the server variables', () => { const document: Partial = { + id: '?http-service-id?', paths: { '/pets': { get: { @@ -893,7 +788,6 @@ describe('transformOas3Operation', () => { expect(transformOas3Operation({ document, path: '/pets', method: 'get' }).servers).toEqual([ { - id: expect.any(String), description: 'Sample Petstore Server Https', url: 'https://petstore.swagger.io/v2', variables: { @@ -911,7 +805,6 @@ describe('transformOas3Operation', () => { }, }, { - id: expect.any(String), description: 'Sample Petstore Server Http', url: 'http://petstore.swagger.io/v2', }, @@ -998,21 +891,16 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: expect.any(String), + id: '?http-operation-id?', method: 'get', path: '/pet', - deprecated: false, - internal: false, request: { body: { - id: expect.any(String), contents: [ { - id: expect.any(String), encodings: [], examples: [ { - id: expect.any(String), key: 'pet-shared', value: { type: 'object', @@ -1025,7 +913,6 @@ describe('transformOas3Operation', () => { }, }, { - id: expect.any(String), key: 'pet-not-shared', value: { 'not-shared': true, @@ -1034,7 +921,6 @@ describe('transformOas3Operation', () => { ], mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: {}, @@ -1049,15 +935,12 @@ describe('transformOas3Operation', () => { }, responses: [ { - id: expect.any(String), code: '200', contents: [ { - id: expect.any(String), encodings: [], examples: [ { - id: expect.any(String), key: 'pet-shared', value: { properties: { @@ -1070,7 +953,6 @@ describe('transformOas3Operation', () => { }, }, { - id: expect.any(String), key: 'pet-not-shared', value: { 'not-shared': true, @@ -1079,7 +961,6 @@ describe('transformOas3Operation', () => { ], mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: {}, @@ -1137,15 +1018,12 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { - id: expect.any(String), contents: [ { - id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -1202,15 +1080,12 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { - id: expect.any(String), contents: [ { - id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -1253,12 +1128,9 @@ describe('transformOas3Operation', () => { method: 'post', document, }), - ).toHaveProperty( - 'request.body', - expect.objectContaining({ - contents: [], - }), - ); + ).toHaveProperty('request.body', { + contents: [], + }); }); describe('OAS 3.1 support', () => { @@ -1339,20 +1211,22 @@ describe('transformOas3Operation', () => { ).toEqual( expect.objectContaining({ responses: [ - expect.objectContaining({ + { code: '200', contents: [ - expect.objectContaining({ + { + encodings: [], + examples: [], + mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }), + }, ], headers: [], - }), + }, ], }), ); @@ -1366,23 +1240,21 @@ describe('transformOas3Operation', () => { ).toEqual( expect.objectContaining({ request: { - body: expect.objectContaining({ + body: { contents: [ expect.objectContaining({ schema: { - 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, }), ], - }), + }, cookie: [], headers: [ expect.objectContaining({ schema: { - 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'string', format: 'email', @@ -1394,19 +1266,19 @@ describe('transformOas3Operation', () => { query: [], }, responses: [ - expect.objectContaining({ + { code: '200', contents: [ expect.objectContaining({ schema: { - 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, }), ], - }), + headers: [], + }, ], }), ); @@ -1450,7 +1322,6 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('responses', [ { - id: expect.any(String), code: '200', contents: expect.any(Array), headers: expect.any(Array), @@ -1460,7 +1331,6 @@ describe('transformOas3Operation', () => { it('should support requestBodies on any method', () => { const document: Partial = { - 'x-stoplight-id': 'abc-def', openapi: '3.1.0', paths: { '/subscribe': { @@ -1493,15 +1363,12 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { - id: expect.any(String), contents: [ { - id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { diff --git a/src/oas3/__tests__/service.test.ts b/src/oas3/__tests__/service.test.ts index 06eb125f..523ac541 100644 --- a/src/oas3/__tests__/service.test.ts +++ b/src/oas3/__tests__/service.test.ts @@ -1,15 +1,6 @@ import { OpenAPIObject } from 'openapi3-ts'; -import { transformOas3Service as _transformOas3Service } from '../service'; - -const transformOas3Service: typeof _transformOas3Service = ({ document, ...opts }) => - _transformOas3Service({ - document: { - 'x-stoplight-id': 'abc', - ...document, - }, - ...opts, - }); +import { transformOas3Service } from '../service'; describe('oas3 service', () => { it('should handle non object security objects', () => { @@ -25,12 +16,11 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '', securitySchemes: [ { - id: expect.any(String), key: 't1', type: 'apiKey', in: undefined, @@ -78,12 +68,11 @@ describe('oas3 service', () => { }, ])('should handle lacking flows for oauth2 security object', document => { expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '', securitySchemes: [ { - id: expect.any(String), flows: {}, key: 't1', type: 'oauth2', @@ -98,7 +87,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '', }); @@ -121,17 +110,15 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: '', version: '1.0', servers: [ { - id: expect.any(String), name: '', url: 'https://petstore.swagger.io/v2', }, { - id: expect.any(String), name: '', url: 'http://petstore.swagger.io/v2', }, @@ -148,7 +135,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '', }); @@ -156,7 +143,7 @@ describe('oas3 service', () => { it('should handle server variables', () => { const document: Partial = { - id: 'abc', + id: '?http-service-id?', name: '', version: '1.0', servers: [ @@ -185,12 +172,11 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '', servers: [ { - id: expect.any(String), description: 'Sample Petstore Server Https', url: 'https://petstore.swagger.io/v2', variables: { @@ -208,7 +194,6 @@ describe('oas3 service', () => { }, }, { - id: expect.any(String), description: 'Sample Petstore Server Http', url: 'http://petstore.swagger.io/v2', }, @@ -255,7 +240,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toEqual({ - id: 'abc', + id: '?http-service-id?', name: '', version: '1.0', summary: 'Very cool API', @@ -275,7 +260,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toEqual({ - id: 'abc', + id: '?http-service-id?', name: '', version: '1.0', license: { @@ -301,7 +286,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', version: '1.0.0', logo: { @@ -325,7 +310,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: 'abc', + id: '?http-service-id?', name: 'no-title', contact: { url: 'https://stoplight.io', diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index 79ea47bb..38f66cf5 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,50 +1,46 @@ import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial, Dictionary, HttpSecurityScheme, Optional } from '@stoplight/types'; +import type { DeepPartial, Dictionary, HttpSecurityScheme } from '@stoplight/types'; import pickBy = require('lodash.pickby'); import type { OpenAPIObject } from 'openapi3-ts'; -import { isNonNullable } from '../guards'; import { entries } from '../utils'; -import { isSecurityScheme } from './guards'; +import { isSecurityScheme, isSecuritySchemeWithKey } from './guards'; export type OperationSecurities = Dictionary[] | undefined; +export type SecurityWithKey = HttpSecurityScheme & { key: string }; export function getSecurities( document: DeepPartial, operationSecurities?: unknown, -): [key: string, security: Omit][][] { +): SecurityWithKey[][] { const definitions = document.components?.securitySchemes; if (!isPlainObject(definitions)) return []; return (Array.isArray(operationSecurities) ? operationSecurities : document.security || []).map(operationSecurity => { return entries(operationSecurity) - .map]>>(([opScheme, scopes]) => { + .map(([opScheme, scopes]) => { const definition = definitions[opScheme]; - if (!isSecurityScheme(definition)) return; - - if (definition.type === 'oauth2') { + if (isSecurityScheme(definition) && definition.type === 'oauth2') { // Put back only the flows that are part of the current definition - return [ - opScheme, - { - ...definition, - flows: Object.fromEntries( - entries(definition.flows).map(([name, flow]) => [ - name, - { - ...flow, - scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), - }, - ]), - ), - }, - ]; + return { + ...definition, + flows: Object.fromEntries( + entries(definition.flows).map(([name, flow]) => [ + name, + { + ...flow, + scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), + }, + ]), + ), + key: opScheme, + }; } - return [opScheme, definition]; + return { ...definition, key: opScheme }; }) - .filter(isNonNullable); + .filter(isSecuritySchemeWithKey); }); } diff --git a/src/oas3/guards.ts b/src/oas3/guards.ts index 4c964fab..3bac2280 100644 --- a/src/oas3/guards.ts +++ b/src/oas3/guards.ts @@ -11,9 +11,14 @@ import type { TagObject, } from 'openapi3-ts'; +import type { SecurityWithKey } from './accessors'; + export const isSecurityScheme = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecuritySchemeObject => isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; +export const isSecuritySchemeWithKey = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecurityWithKey => + isSecurityScheme(maybeSecurityScheme) && typeof maybeSecurityScheme.key === 'string'; + export const isBaseParameterObject = ( maybeBaseParameterObject: unknown, ): maybeBaseParameterObject is BaseParameterObject => diff --git a/src/oas3/index.ts b/src/oas3/index.ts index f2e98a7a..2e002f33 100644 --- a/src/oas3/index.ts +++ b/src/oas3/index.ts @@ -1,2 +1,3 @@ export * from './operation'; export * from './service'; +export * from './transformers/request'; diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index ca08e7ca..f423a26c 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,42 +1,54 @@ -import type { DeepPartial, IHttpOperation } from '@stoplight/types'; +import { IHttpOperation } from '@stoplight/types'; +import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; import pickBy = require('lodash.pickby'); -import type { OpenAPIObject } from 'openapi3-ts'; -import { createContext, DEFAULT_ID_GENERATOR } from '../context'; +import { createContext } from '../context'; import { isNonNullable } from '../guards'; -import { transformOasOperation, transformOasOperations } from '../oas'; -import { resolveRef } from '../oas/resolver'; +import { transformOasOperations } from '../oas'; +import { getExtensions } from '../oas/accessors'; +import { translateToTags } from '../oas/tags'; import type { Oas3HttpOperationTransformer } from '../oas/types'; -import { Fragment } from '../types'; +import { maybeResolveLocalRef } from '../utils'; import { translateToCallbacks } from './transformers/callbacks'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; import { translateToServers } from './transformers/servers'; -export function transformOas3Operations(document: DeepPartial): IHttpOperation[] { +export function transformOas3Operations(document: OpenAPIObject): IHttpOperation[] { return transformOasOperations(document, transformOas3Operation); } -export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document: _document, path, method }) => { - const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); - const httpOperation = transformOasOperation.call(ctx, path, method); - const pathObj = ctx.maybeResolveLocalRef(ctx.document.paths![path]) as Fragment; - const operation = ctx.maybeResolveLocalRef(pathObj[method]) as Fragment; +export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, path, method }) => { + const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]) as PathsObject; + if (typeof pathObj !== 'object' || pathObj === null) { + throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); + } - return { - ...httpOperation, + const operation = maybeResolveLocalRef(document, pathObj[method]) as OperationObject; + if (!operation) { + throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); + } + const ctx = createContext(document); + + const httpOperation: IHttpOperation = { + id: '?http-operation-id?', + iid: operation.operationId, + description: operation.description, + deprecated: operation.deprecated, + internal: operation['x-internal'], + method, + path, + summary: operation.summary, responses: translateToResponses.call(ctx, operation.responses), request: translateToRequest.call(ctx, pathObj, operation), + callbacks: translateToCallbacks.call(ctx, operation.callbacks), + tags: translateToTags.call(ctx, operation.tags), security: translateToSecurities.call(ctx, operation.security), + extensions: getExtensions(operation), servers: translateToServers.call(ctx, pathObj, operation), - - ...pickBy( - { - callbacks: translateToCallbacks.call(ctx, operation.callbacks), - }, - isNonNullable, - ), }; + + return pickBy(httpOperation, isNonNullable) as IHttpOperation; }; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index 27a8dcdb..acce098f 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,22 +1,20 @@ import { isPlainObject } from '@stoplight/json'; -import type { HttpSecurityScheme, Optional } from '@stoplight/types'; - -import { createContext, DEFAULT_ID_GENERATOR, withContext } from '../context'; +import type { Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { createContext } from '../context'; import { isNonNullable } from '../guards'; -import { resolveRef } from '../oas/resolver'; import { transformOasService } from '../oas/service'; import type { Oas3HttpServiceTransformer } from '../oas/types'; import { ArrayCallbackParameters } from '../types'; import { entries } from '../utils'; +import { SecurityWithKey } from './accessors'; import { isSecurityScheme } from './guards'; import { translateToSingleSecurity } from './transformers/securities'; import { translateToServer } from './transformers/servers'; import { Oas3TranslateFunction } from './types'; -import pickBy = require('lodash.pickby'); -export const transformOas3Service: Oas3HttpServiceTransformer = ({ document: _document }) => { - const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); - const { document } = ctx; +export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) => { + const ctx = createContext(document); const httpService = transformOasService.call(ctx); if (typeof document.info?.summary === 'string') { @@ -86,10 +84,16 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document: _do return httpService; }; -const translateSecurityScheme = withContext< - Oas3TranslateFunction, Optional> ->(function ([key, definition]) { +const translateSecurityScheme: Oas3TranslateFunction< + ArrayCallbackParameters<[name: string, scheme: unknown]>, + Optional +> = function ([key, definition]) { if (!isSecurityScheme(definition)) return; - return translateToSingleSecurity.call(this, [key, definition]); -}); + const transformed = translateToSingleSecurity.call(this, definition); + if (transformed && 'key' in transformed) { + transformed.key = key; + } + + return transformed; +}; diff --git a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap index aa6e45c8..04e8e103 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap @@ -10,24 +10,20 @@ Object { "encodings": Array [], "examples": Array [ Object { - "id": Any, "key": "a", "summary": "example summary", "value": "hey", }, Object { - "id": Any, "key": "__default", "value": Object {}, }, ], "explode": true, - "id": Any, "name": "header-name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, "style": "simple", } @@ -47,14 +43,12 @@ Object { "encodings": Array [], "examples": Array [ Object { - "id": Any, "key": "a", "summary": "example summary", "value": "hey", }, ], "explode": false, - "id": Any, "name": "h1", "style": "simple", }, @@ -66,17 +60,14 @@ Object { ], "examples": Array [ Object { - "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], - "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, } `; @@ -91,7 +82,6 @@ Object { Object { "encodings": Array [], "examples": Array [], - "id": Any, "name": "h1", "style": "simple", }, @@ -107,7 +97,6 @@ Object { Object { "encodings": Array [], "examples": Array [], - "id": Any, "name": "h1", "style": "simple", }, @@ -119,17 +108,14 @@ Object { ], "examples": Array [ Object { - "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], - "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, } `; @@ -139,17 +125,14 @@ Object { "encodings": Array [], "examples": Array [ Object { - "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], - "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, } `; @@ -159,16 +142,13 @@ Object { "encodings": Array [], "examples": Array [ Object { - "id": Any, "key": "default", "value": "hey", }, ], - "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, } `; @@ -186,17 +166,14 @@ Object { ], "examples": Array [ Object { - "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], - "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", - "x-stoplight-id": Any, }, } `; diff --git a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap index afd99f82..4afeb24b 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -4,20 +4,9 @@ exports[`translateOas3ToRequest give a request body should translate it 1`] = ` Object { "body": Object { "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "id": Any, - "mediaType": "content-a", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "deprecated": true, - "x-stoplight-id": Any, - }, - }, + "translateMediaTypeObject([\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}], 0, [[\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}]])", ], "description": "descr", - "id": Any, "required": true, }, "cookie": Array [], @@ -31,7 +20,6 @@ exports[`translateOas3ToRequest given no request body should translate parameter Object { "body": Object { "contents": Array [], - "id": "c27fe9940305e", }, "cookie": Array [], "headers": Array [ @@ -45,9 +33,7 @@ Object { "description": "descr", "examples": Array [], "explode": false, - "id": "824b189f6d434", "name": "param-name-3", - "required": false, "style": "simple", }, ], @@ -63,9 +49,7 @@ Object { "description": "descr", "examples": Array [], "explode": false, - "id": "7e5f6c5bf0edf", "name": "param-name-2", - "required": false, "style": "simple", }, Object { @@ -78,9 +62,7 @@ Object { "description": "descr", "examples": Array [], "explode": false, - "id": "7e8f6c5bf0b36", "name": "param-name-1", - "required": false, "style": "simple", }, ], diff --git a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap index e4df9bec..54946b4f 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -1,97 +1,84 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`translateToOas3Responses given a response in dictionary should translate 1`] = ` -Object { - "code": "200", - "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [ - Object { - "id": Any, - "key": "default", - "value": "dumb", - }, - ], - "id": Any, - "mediaType": "fake-content-type-200", - }, - ], - "description": "descr 200", - "headers": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "id": Any, - "name": "fake-header-name-200", - "style": "simple", - }, - ], - "id": Any, -} -`; - -exports[`translateToOas3Responses given a response in dictionary should translate 2`] = ` -Object { - "code": "default", - "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "id": Any, - "mediaType": "fake-content-type", - }, - ], - "description": "descr", - "headers": Array [ - Object { - "description": "calls per hour allowed by the user", - "encodings": Array [], - "examples": Array [ - Object { - "id": Any, - "key": "__default", - "value": 1000, +Array [ + Object { + "code": "200", + "contents": Array [ + Object { + "encodings": Array [], + "examples": Array [ + Object { + "key": "default", + "value": "dumb", + }, + ], + "mediaType": "fake-content-type-200", + }, + ], + "description": "descr 200", + "headers": Array [ + Object { + "encodings": Array [], + "examples": Array [], + "name": "fake-header-name-200", + "style": "simple", + }, + ], + }, + Object { + "code": "default", + "contents": Array [ + Object { + "encodings": Array [], + "examples": Array [], + "mediaType": "fake-content-type", + }, + ], + "description": "descr", + "headers": Array [ + Object { + "description": "calls per hour allowed by the user", + "encodings": Array [], + "examples": Array [ + Object { + "key": "__default", + "value": 1000, + }, + ], + "explode": false, + "name": "fake-header-name-1", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer", }, - ], - "explode": false, - "id": Any, - "name": "fake-header-name-1", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": "integer", - "x-stoplight-id": Any, + "style": "simple", }, - "style": "simple", - }, - Object { - "description": "calls per hour allowed by the user", - "encodings": Array [], - "examples": Array [ - Object { - "id": Any, - "key": "__default", - "value": 1000, + Object { + "description": "calls per hour allowed by the user", + "encodings": Array [], + "examples": Array [ + Object { + "key": "__default", + "value": 1000, + }, + ], + "explode": false, + "name": "fake-header-name-2", + "required": true, + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer", }, - ], - "explode": false, - "id": Any, - "name": "fake-header-name-2", - "required": true, - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": "integer", - "x-stoplight-id": Any, + "style": "simple", }, - "style": "simple", - }, - ], - "id": Any, -} + ], + }, +] `; diff --git a/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap index 7bb51f80..94ef254f 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap @@ -4,7 +4,6 @@ exports[`translateToServers translate single ServerObject to IServer 1`] = ` Array [ Object { "description": "description", - "id": "6f520f1ba667c", "url": "http://stoplight.io/path", "variables": Object { "a": Object { diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index 47017ebd..d22bf44a 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -1,17 +1,16 @@ import type { SchemaObject } from 'openapi3-ts'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateHeaderObject as _translateHeaderObject, translateMediaTypeObject as _translateMediaTypeObject, } from '../content'; const translateMediaTypeObject = (document: any, object: unknown, key: string) => - _translateMediaTypeObject.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); + _translateMediaTypeObject.call(createContext(document), [key, object], 0, []); const translateHeaderObject = (object: unknown, key: string) => - _translateHeaderObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); + _translateHeaderObject.call(createContext({}), [key, object], 0, []); describe('translateMediaTypeObject', () => { afterEach(() => { @@ -24,7 +23,6 @@ describe('translateMediaTypeObject', () => { it('given empty object, should return nothing', () => { expect(translateMediaTypeObject({}, {}, 'mediaType')).toStrictEqual({ - id: expect.any(String), encodings: [], examples: [], mediaType: 'mediaType', @@ -41,7 +39,6 @@ describe('translateMediaTypeObject', () => { 'mediaType', ), ).toStrictEqual({ - id: expect.any(String), encodings: [], examples: [], mediaType: 'mediaType', @@ -59,17 +56,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot({ - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); it('given multiple examples should translate to IHttpContent', () => { @@ -83,17 +70,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot({ - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); it('given encodings should translate each encoding to array item', () => { @@ -132,33 +109,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot({ - id: expect.any(String), - encodings: [ - { - headers: [ - { - id: expect.any(String), - }, - ], - }, - { - headers: [ - { - id: expect.any(String), - }, - ], - }, - ], - examples: [ - { - id: expect.any(String), - }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); it('given complex nested media type object should translate correctly', () => { @@ -192,31 +143,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot({ - id: expect.any(String), - encodings: [ - { - headers: [ - { - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - ], - }, - ], - }, - ], - examples: [ - { - id: expect.any(String), - }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); it('should skip nullish headers', () => { @@ -238,17 +165,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot({ - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); it('given encoding with no style it should not throw an error', () => { @@ -340,7 +257,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toHaveProperty('examples', [{ id: expect.any(String), key: 'default', value: defaultExample }]); + ).toHaveProperty('examples', [{ key: 'default', value: defaultExample }]); }); }); @@ -362,8 +279,8 @@ describe('translateMediaTypeObject', () => { 'mediaType', ), ).toHaveProperty('examples', [ - { id: expect.any(String), key: 'default', value: { name: 'root default value' } }, - { id: expect.any(String), key: 'example', value: { name: 'root example value' } }, + { key: 'default', value: { name: 'root default value' } }, + { key: 'example', value: { name: 'root example value' } }, ]); }); }); @@ -460,20 +377,7 @@ describe('translateHeaderObject', () => { }, 'header-name', ), - ).toMatchSnapshot({ - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - { - id: expect.any(String), - }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }); + ).toMatchSnapshot(); }); it('should handle nullish value gracefully', () => { diff --git a/src/oas3/transformers/__tests__/request.test.ts b/src/oas3/transformers/__tests__/request.test.ts index 26e0c3dc..0be85e70 100644 --- a/src/oas3/transformers/__tests__/request.test.ts +++ b/src/oas3/transformers/__tests__/request.test.ts @@ -1,11 +1,21 @@ -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { mockPassthroughImplementation } from '@stoplight/test-utils'; + +import { createContext } from '../../../context'; +import { translateMediaTypeObject } from '../content'; import { translateToRequest as _translateToRequest } from '../request'; -const translateToRequest = (path: Record, operation: Record) => - _translateToRequest.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), path, operation); +jest.mock('../content'); + +const translateToRequest = (path: Record, operation: Record) => { + const ctx = createContext({ paths: { '/api': path } }); + return _translateToRequest.call(ctx, path, operation); +}; describe('translateOas3ToRequest', () => { + beforeEach(() => { + mockPassthroughImplementation(translateMediaTypeObject); + }); + it('given no request body should translate parameters', () => { const operation = { parameters: [ @@ -71,18 +81,6 @@ describe('translateOas3ToRequest', () => { post: operation, }; - expect(translateToRequest(path, operation)).toMatchSnapshot({ - body: { - id: expect.any(String), - contents: [ - { - id: expect.any(String), - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - ], - }, - }); + expect(translateToRequest(path, operation)).toMatchSnapshot(); }); }); diff --git a/src/oas3/transformers/__tests__/responses.test.ts b/src/oas3/transformers/__tests__/responses.test.ts index 55531a4a..9f5a929d 100644 --- a/src/oas3/transformers/__tests__/responses.test.ts +++ b/src/oas3/transformers/__tests__/responses.test.ts @@ -1,12 +1,11 @@ import type { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateToResponses as _translateToResponses } from '../responses'; const translateToResponses = (document: DeepPartial, responses: unknown) => - _translateToResponses.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), responses); + _translateToResponses.call(createContext(document), responses); describe('translateToOas3Responses', () => { it('given empty dictionary should return empty array', () => { @@ -14,99 +13,49 @@ describe('translateToOas3Responses', () => { }); it('given a response in dictionary should translate', () => { - const responses = translateToResponses( - {}, - { - default: { - content: { - 'fake-content-type': {}, - }, - description: 'descr', - headers: { - 'fake-header-name-1': { - description: 'calls per hour allowed by the user', - schema: { - type: 'integer', - format: 'int32', - }, - example: 1000, + expect( + translateToResponses( + {}, + { + default: { + content: { + 'fake-content-type': {}, }, - 'fake-header-name-2': { - description: 'calls per hour allowed by the user', - schema: { - type: 'integer', - format: 'int32', + description: 'descr', + headers: { + 'fake-header-name-1': { + description: 'calls per hour allowed by the user', + schema: { + type: 'integer', + format: 'int32', + }, + example: 1000, + }, + 'fake-header-name-2': { + description: 'calls per hour allowed by the user', + schema: { + type: 'integer', + format: 'int32', + }, + required: true, + example: 1000, }, - required: true, - example: 1000, - }, - }, - }, - 200: { - content: { - 'fake-content-type-200': { - example: 'dumb', }, }, - description: 'descr 200', - headers: { - 'fake-header-name-200': {}, - }, - }, - }, - ); - - expect(responses).toHaveLength(2); - expect(responses[0]).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - examples: [ - { - id: expect.any(String), - }, - ], - }, - ], - headers: [ - { - id: expect.any(String), - }, - ], - }); - expect(responses[1]).toMatchSnapshot({ - id: expect.any(String), - contents: [ - { - id: expect.any(String), - }, - ], - headers: [ - { - id: expect.any(String), - examples: [ - { - id: expect.any(String), + 200: { + content: { + 'fake-content-type-200': { + example: 'dumb', + }, }, - ], - schema: { - 'x-stoplight-id': expect.any(String), - }, - }, - { - id: expect.any(String), - examples: [ - { - id: expect.any(String), + description: 'descr 200', + headers: { + 'fake-header-name-200': {}, }, - ], - schema: { - 'x-stoplight-id': expect.any(String), }, }, - ], - }); + ), + ).toMatchSnapshot(); }); it('given a response with nullish headers in dictionary should translate', () => { @@ -123,7 +72,6 @@ describe('translateToOas3Responses', () => { ), ).toStrictEqual([ { - id: expect.any(String), code: '200', contents: [], headers: [], @@ -144,7 +92,6 @@ describe('translateToOas3Responses', () => { ), ).toStrictEqual([ { - id: expect.any(String), code: '201', contents: [], description: 'description 201', @@ -202,15 +149,12 @@ describe('translateToOas3Responses', () => { expect(translateToResponses(document, document.paths!['/user'].get.responses)).toEqual([ { - id: expect.any(String), code: '200', contents: [ { - id: expect.any(String), encodings: [], examples: [ { - id: expect.any(String), key: 'my-example', value: { id: 1, @@ -219,7 +163,6 @@ describe('translateToOas3Responses', () => { ], mediaType: 'application/json', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { id: { @@ -265,16 +208,13 @@ describe('translateToOas3Responses', () => { const expected = [ { - id: expect.any(String), code: '200', contents: [], description: 'OK', headers: [ { - id: expect.any(String), name: 'X-Page', schema: { - 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', }, @@ -284,7 +224,6 @@ describe('translateToOas3Responses', () => { encodings: [], examples: [ { - id: expect.any(String), key: '__default', value: 3, }, diff --git a/src/oas3/transformers/__tests__/securities.test.ts b/src/oas3/transformers/__tests__/securities.test.ts index f24f7c46..b682c291 100644 --- a/src/oas3/transformers/__tests__/securities.test.ts +++ b/src/oas3/transformers/__tests__/securities.test.ts @@ -1,13 +1,12 @@ import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { OperationSecurities } from '../../accessors'; import { translateToSecurities as _translateToSecurities } from '../securities'; const translateToSecurities = (document: DeepPartial, operationSecurities: OperationSecurities) => - _translateToSecurities.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), operationSecurities); + _translateToSecurities.call(createContext(document), operationSecurities); describe('securities', () => { describe('translateToSecurities', () => { @@ -30,7 +29,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), key: 'basic-security', type: 'http', description: 'a description', @@ -59,7 +57,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), key: 'digest-security', type: 'http', description: 'a description', @@ -89,7 +86,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), key: 'bearer-security', type: 'http', description: 'a description', @@ -119,7 +115,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), key: 'openIdConnect-security', type: 'openIdConnect', description: 'a description', @@ -149,7 +144,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), name: 'a name', type: 'apiKey', in: 'header', @@ -179,7 +173,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), key: 'basic-security', type: 'http', description: 'a description', @@ -214,7 +207,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-flow-security', @@ -248,7 +240,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { password: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'password-flow-security', @@ -282,7 +273,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { clientCredentials: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'clientCredentials-flow-security', @@ -317,7 +307,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), description: 'a description', flows: { authorizationCode: { @@ -382,7 +371,6 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), type: 'http', description: 'a description', key: 'http-security', @@ -390,7 +378,6 @@ describe('securities', () => { ], [ { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', @@ -399,7 +386,6 @@ describe('securities', () => { ], [ { - id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', @@ -418,20 +404,17 @@ describe('securities', () => { ).toEqual([ [ { - id: expect.any(String), type: 'http', description: 'a description', key: 'http-security', }, { - id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', type: 'oauth2', }, { - id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', diff --git a/src/oas3/transformers/__tests__/servers.test.ts b/src/oas3/transformers/__tests__/servers.test.ts index 543249b0..d6573d06 100644 --- a/src/oas3/transformers/__tests__/servers.test.ts +++ b/src/oas3/transformers/__tests__/servers.test.ts @@ -1,18 +1,12 @@ import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; -import { resolveRef } from '../../../oas/resolver'; +import { createContext } from '../../../context'; import { translateToServers as _translateToServers } from '../servers'; const translateToServers = ( document: DeepPartial & { paths: { '/pet': { get: Record } } }, -) => - _translateToServers.call( - createContext(document, resolveRef, DEFAULT_ID_GENERATOR), - document.paths['/pet'], - document.paths['/pet'].get, - ); +) => _translateToServers.call(createContext(document), document.paths['/pet'], document.paths['/pet'].get); describe('translateToServers', () => { it('translate single ServerObject to IServer', () => { @@ -74,7 +68,6 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { - id: expect.any(String), description: 'description', url: 'http://stoplight.io/path', variables: { @@ -119,7 +112,6 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { - id: expect.any(String), description: 'description', url: 'http://stoplight.io/pet.get', }, @@ -150,7 +142,6 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { - id: expect.any(String), description: 'description', url: 'http://stoplight.io/pet', }, diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index f96cc07f..7b33152f 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -1,5 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import { + Dictionary, HttpParamStyles, IHttpEncoding, IHttpHeaderParam, @@ -10,15 +11,12 @@ import { import type { JSONSchema7 } from 'json-schema'; import pickBy = require('lodash.pickby'); -import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; -import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { ArrayCallbackParameters, Fragment } from '../../types'; -import { entries } from '../../utils'; +import { entries, maybeResolveLocalRef } from '../../utils'; import { isHeaderObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -import { translateToExample } from './examples'; const ACCEPTABLE_STYLES: (string | undefined)[] = [ HttpParamStyles.Form, @@ -27,7 +25,7 @@ const ACCEPTABLE_STYLES: (string | undefined)[] = [ HttpParamStyles.DeepObject, ]; -function hasAcceptableStyle( +function isAcceptableStyle( encodingPropertyObject: T, ): encodingPropertyObject is T & { style: @@ -39,14 +37,12 @@ function hasAcceptableStyle( return typeof encodingPropertyObject.style === 'string' && ACCEPTABLE_STYLES.includes(encodingPropertyObject.style); } -const translateEncodingPropertyObject = withContext< - Oas3TranslateFunction< - ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, - Optional - > ->(function ([property, encodingPropertyObject]) { +const translateEncodingPropertyObject: Oas3TranslateFunction< + ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, + Optional +> = function ([property, encodingPropertyObject]) { if (!isPlainObject(encodingPropertyObject)) return; - if (!hasAcceptableStyle(encodingPropertyObject)) return; + if (!isAcceptableStyle(encodingPropertyObject)) return; return { property, @@ -68,19 +64,16 @@ const translateEncodingPropertyObject = withContext< isString, ), }; -}); +}; -export const translateHeaderObject = withContext< +export const translateHeaderObject = < Oas3TranslateFunction, Optional> ->(function ([name, unresolvedHeaderObject]) { +>function ([name, unresolvedHeaderObject]) { const headerObject = this.maybeResolveLocalRef(unresolvedHeaderObject); if (!isPlainObject(headerObject)) return; - const id = this.generateId(`http_header-${this.parentId}-${name}`); - if (!isHeaderObject(headerObject)) { return { - id, encodings: [], examples: [], name, @@ -93,7 +86,6 @@ export const translateHeaderObject = withContext< const contentValue = isPlainObject(contentObject) ? Object.values(contentObject)[0] : null; const baseContent: IHttpHeaderParam = { - id, name, style: HttpParamStyles.Simple, explode: !!headerObject.explode, @@ -136,14 +128,14 @@ export const translateHeaderObject = withContext< } if ('example' in contentValue) { - examples.push(translateToDefaultExample.call(this, '__default_content', contentValue.example)); + examples.push(transformDefaultExample.call(this, '__default_content', contentValue.example)); } } examples.push(...entries(headerObject.examples).map(translateToExample, this).filter(isNonNullable)); if ('example' in headerObject) { - examples.push(translateToDefaultExample.call(this, '__default', headerObject.example)); + examples.push(transformDefaultExample.call(this, '__default', headerObject.example)); } return { @@ -151,33 +143,34 @@ export const translateHeaderObject = withContext< encodings, examples, }; -}); +}; -const translateSchemaMediaTypeObject = withContext>>( - function (schema) { - if (!isPlainObject(schema)) return; +const translateSchemaMediaTypeObject: Oas3TranslateFunction<[schema: unknown], Optional> = function ( + schema, +) { + if (!isPlainObject(schema)) return; - return translateSchemaObject.call(this, schema); - }, -); + return translateSchemaObject.call(this, schema); +}; -export const translateMediaTypeObject = withContext< - Oas3TranslateFunction, Optional> ->(function ([mediaType, mediaObject]) { +export const translateMediaTypeObject: Oas3TranslateFunction< + ArrayCallbackParameters<[mediaType: string, mediaObject: unknown]>, + Optional +> = function ([mediaType, mediaObject]) { if (!isPlainObject(mediaObject)) return; - const id = this.generateId(`http_media-${this.parentId}-${mediaType}`); - const { schema, encoding, examples } = mediaObject; + const resolvedMediaObject = resolveMediaObject(this.document, mediaObject); + const { schema, encoding, examples } = resolvedMediaObject; + const jsonSchema = translateSchemaMediaTypeObject.call(this, schema); - const defaultExample = 'example' in mediaObject ? mediaObject.example : jsonSchema?.examples?.[0]; + const example = resolvedMediaObject.example || jsonSchema?.examples?.[0]; return { - id, mediaType, // Note that I'm assuming all references are resolved examples: [ - defaultExample !== undefined ? translateToDefaultExample.call(this, 'default', defaultExample) : undefined, + example ? transformDefaultExample.call(this, 'default', example) : undefined, ...entries(examples).map(translateToExample, this), ].filter(isNonNullable), encodings: entries(encoding).map(translateEncodingPropertyObject, this).filter(isNonNullable), @@ -189,4 +182,51 @@ export const translateMediaTypeObject = withContext< isNonNullable, ), }; -}); +}; + +function resolveMediaObject(document: unknown, maybeMediaObject: Dictionary) { + const mediaObject = { ...maybeMediaObject }; + if (isPlainObject(mediaObject.schema)) { + mediaObject.schema = maybeResolveLocalRef(document, mediaObject.schema); + } + + if (isPlainObject(mediaObject.examples)) { + const examples = { ...mediaObject.examples }; + mediaObject.examples = examples; + for (const [exampleName, exampleValue] of entries(examples)) { + examples[exampleName] = maybeResolveLocalRef(document, exampleValue); + } + } + + return mediaObject; +} + +const transformDefaultExample: Oas3TranslateFunction<[key: string, value: unknown], INodeExample> = function ( + key, + value, +) { + return { + value, + key, + }; +}; + +const translateToExample: Oas3TranslateFunction< + ArrayCallbackParameters<[key: string, example: unknown]>, + Optional +> = function ([key, example]) { + if (!isPlainObject(example)) return; + + return { + value: example.value, + key, + + ...pickBy( + { + summary: example.summary, + description: example.description, + }, + isString, + ), + }; +}; diff --git a/src/oas3/transformers/examples.ts b/src/oas3/transformers/examples.ts deleted file mode 100644 index 5bfb8032..00000000 --- a/src/oas3/transformers/examples.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { isPlainObject } from '@stoplight/json'; -import { INodeExample, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); - -import { withContext } from '../../context'; -import { isString } from '../../guards'; -import { getSharedKey } from '../../oas/resolver'; -import type { ArrayCallbackParameters } from '../../types'; -import type { Oas3TranslateFunction } from '../types'; - -export const translateToExample = withContext< - Oas3TranslateFunction, Optional> ->(function ([key, example]) { - const resolvedExample = this.maybeResolveLocalRef(example); - - if (!isPlainObject(resolvedExample)) return; - - const actualKey = this.context === 'service' ? getSharedKey(resolvedExample) : key; - - return { - id: this.generateId(`example-${this.parentId}-${actualKey}`), - value: resolvedExample.value, - key, - - ...pickBy( - { - summary: resolvedExample.summary, - description: resolvedExample.description, - }, - isString, - ), - }; -}); diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index ee64fb8a..f7997316 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -4,127 +4,140 @@ import type { IHttpOperationRequest, IHttpOperationRequestBody, IHttpParam, + INodeExample, + INodeExternalExample, Optional, } from '@stoplight/types'; import { HttpParamStyles } from '@stoplight/types'; import type { JSONSchema7 } from 'json-schema'; import type { ParameterObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); -import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; import { OasVersion } from '../../oas'; -import { createOasParamsIterator } from '../../oas/accessors'; +import { getValidOasParameters } from '../../oas/accessors'; import { isValidParamStyle } from '../../oas/guards'; -import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; +import type { ArrayCallbackParameters, Fragment } from '../../types'; import { entries } from '../../utils'; import { isRequestBodyObject } from '../guards'; -import { Oas3TranslateFunction } from '../types'; +import type { Oas3TranslateFunction } from '../types'; import { translateMediaTypeObject } from './content'; -import { translateToExample } from './examples'; -import pickBy = require('lodash.pickby'); - -export const translateRequestBody = withContext< - Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> ->(function (requestBodyObject) { - const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); - const id = this.generateId(`http_request_body-${this.parentId}`); - - if (isRequestBodyObject(resolvedRequestBodyObject)) { - return { - id, - contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), - ...pickBy( - { - required: resolvedRequestBodyObject.required, - description: resolvedRequestBodyObject.description, - }, - isNonNullable, - ), - }; - } - return { id, contents: [] }; -}); +export const translateRequestBody: Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> = + function (requestBodyObject) { + const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); + + if (isRequestBodyObject(resolvedRequestBodyObject)) { + return { + contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), + + ...pickBy( + { + required: resolvedRequestBodyObject.required, + description: resolvedRequestBodyObject.description, + }, + isNonNullable, + ), + }; + } + + return { contents: [] }; + }; -const translateParameterObjectSchema = withContext< - Oas3TranslateFunction<[parameterObject: Record], Optional> ->(function (parameterObject) { +const translateParameterObjectSchema: Oas3TranslateFunction< + [parameterObject: Fragment], + Optional +> = function (parameterObject) { if (!isPlainObject(parameterObject.schema)) return; return translateSchemaObject.call(this, { ...parameterObject.schema, ...('example' in parameterObject ? { example: parameterObject.example } : null), }); -}); +}; -export const translateParameterObject = withContext< - Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> ->(function (parameterObject) { - const kind = parameterObject.in === 'path' ? 'path_param' : parameterObject.in; - const name = parameterObject.name; - const id = this.generateId(`http_${kind}-${this.parentId}-${name}`); - const schema = translateParameterObjectSchema.call(this, parameterObject); +const translateToExample: Oas3TranslateFunction< + ArrayCallbackParameters<[key: string, example: unknown]>, + Optional +> = function ([key, example]) { + if (!isPlainObject(example)) return; - const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); - const hasDefaultExample = examples.some(({ key }) => key.includes('default')); + if (!('value' in example) && typeof example.externalValue !== 'string') return; return { - id, - name, - deprecated: !!parameterObject.deprecated, - required: !!parameterObject.required, - style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, - explode: !!(parameterObject.explode ?? parameterObject.style === HttpParamStyles.Form), - examples: [ - !hasDefaultExample && parameterObject.example !== undefined - ? translateToDefaultExample.call(this, 'default', parameterObject.example) - : undefined, - ...examples, - ].filter(isNonNullable), + key, - ...pickBy( - { - description: parameterObject.description, - }, - isString, - ), + ...(typeof example.externalValue === 'string' + ? { externalValue: example.externalValue } + : { value: example.value }), ...pickBy( { - schema, - content: parameterObject.content, + summary: example.summary, + description: example.description, }, - isPlainObject, + isString, ), }; -}); - -const iterateOasParams = createOasParamsIterator(OasVersion.OAS3); - -export const translateToRequest = withContext< - Oas3TranslateFunction<[path: Record, operation: Record], IHttpOperationRequest> ->(function (path, operation) { - const params: Omit & { header: IHttpHeaderParam[] } = { - header: [], - query: [], - cookie: [], - path: [], +}; + +export const translateParameterObject: Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> = + function (parameterObject) { + const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); + + const hasDefaultExample = examples.some(({ key }) => key.includes('default')); + const schema = translateParameterObjectSchema.call(this, parameterObject); + + return { + name: parameterObject.name, + deprecated: !!parameterObject.deprecated, + style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, + explode: !!(parameterObject.explode ?? parameterObject.style === HttpParamStyles.Form), + examples: + 'example' in parameterObject && !hasDefaultExample + ? [{ key: 'default', value: parameterObject.example }, ...examples] + : examples, + + ...pickBy( + { + description: parameterObject.description, + }, + isString, + ), + + ...pickBy( + { + schema, + content: parameterObject.content, + }, + isPlainObject, + ), + }; }; - for (const param of iterateOasParams.call(this, path, operation)) { - const { in: key } = param; - const target = params[key]; - if (!Array.isArray(target)) continue; +export const translateToRequest: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IHttpOperationRequest> = + function (path, operation) { + const params: Omit & { header: IHttpHeaderParam[] } = { + header: [], + query: [], + cookie: [], + path: [], + }; - target.push(translateParameterObject.call(this, param) as any); - } + for (const param of getValidOasParameters(this.document, OasVersion.OAS3, operation.parameters, path.parameters)) { + const { in: key } = param; + const target = params[key]; + if (!Array.isArray(target)) continue; - return { - body: translateRequestBody.call(this, operation?.requestBody), - headers: params.header, - query: params.query, - cookie: params.cookie, - path: params.path, + target.push(translateParameterObject.call(this, param) as any); + } + + return { + body: translateRequestBody.call(this, operation?.requestBody), + headers: params.header, + query: params.query, + cookie: params.cookie, + path: params.path, + }; }; -}); diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index a569b283..2f7225f9 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,28 +1,21 @@ import type { IHttpOperationResponse, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); -import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import { getSharedKey } from '../../oas/resolver'; -import { ArrayCallbackParameters } from '../../types'; +import type { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; import { isResponseObject } from '../guards'; -import { Oas3TranslateFunction } from '../types'; +import type { Oas3TranslateFunction } from '../types'; import { translateHeaderObject, translateMediaTypeObject } from './content'; -const translateToResponse = withContext< - Oas3TranslateFunction< - ArrayCallbackParameters<[statusCode: string, response: unknown]>, - Optional - > ->(function ([statusCode, response]) { +const translateToResponse: Oas3TranslateFunction< + ArrayCallbackParameters<[statusCode: string, response: unknown]>, + Optional +> = function ([statusCode, response]) { const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; - const actualKey = this.context === 'service' ? getSharedKey(resolvedResponse) : statusCode; - return { - id: this.generateId(`http_response-${this.parentId}-${actualKey}`), code: statusCode, headers: entries(resolvedResponse.headers).map(translateHeaderObject, this).filter(isNonNullable), contents: entries(resolvedResponse.content).map(translateMediaTypeObject, this).filter(isNonNullable), @@ -34,7 +27,7 @@ const translateToResponse = withContext< isString, ), }; -}); +}; export const translateToResponses: Oas3TranslateFunction<[responses: unknown], IHttpOperationResponse[]> = function ( responses, diff --git a/src/oas3/transformers/securities.ts b/src/oas3/transformers/securities.ts index cfd11162..53618e93 100644 --- a/src/oas3/transformers/securities.ts +++ b/src/oas3/transformers/securities.ts @@ -1,34 +1,27 @@ import { isPlainObject } from '@stoplight/json'; import type { IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; -import { HttpSecurityScheme } from '@stoplight/types'; import type { SecuritySchemeObject } from 'openapi3-ts'; -import { withContext } from '../../context'; import { isNonNullable } from '../../guards'; import { ArrayCallbackParameters } from '../../types'; -import { getSecurities } from '../accessors'; +import { getSecurities, SecurityWithKey } from '../accessors'; import { isOAuthFlowObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], HttpSecurityScheme[][]> = +export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = function (operationSecurities) { const securities = getSecurities(this.document, operationSecurities); return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); }; -export const translateToSingleSecurity = withContext< - Oas3TranslateFunction< - [ - ArrayCallbackParameters< - [key: string, security: SecuritySchemeObject | (Omit & { type: 'mutualTLS' })] - >[0], - ], - Optional - > ->(function ([key, securityScheme]) { - const baseObject: { id: string; key: string; description?: string } = { - id: this.generateId(`http_security-${this.ids.service}-${key}`), +export const translateToSingleSecurity: Oas3TranslateFunction< + [ArrayCallbackParameters & { type: 'mutualTLS' })>[0]], + Optional +> = function (securityScheme) { + const { key } = securityScheme; + + const baseObject: { key: string; description?: string } = { key, }; @@ -86,7 +79,7 @@ export const translateToSingleSecurity = withContext< } return undefined; -}); +}; function transformFlows(flows: Optional): IOauthFlowObjects { const transformedFlows: IOauthFlowObjects = {}; diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index 6e9f5265..9a22e861 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,41 +1,38 @@ import type { INodeVariable, IServer, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); -import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import { ArrayCallbackParameters } from '../../types'; +import { ArrayCallbackParameters, Fragment } from '../../types'; import { entries } from '../../utils'; import { isServerObject, isServerVariableObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -export const translateToServers = withContext< - Oas3TranslateFunction<[path: Record, operation: Record], IServer[]> ->(function (path, operation) { +export const translateToServers: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IServer[]> = function ( + path, + operation, +) { let servers; if (Array.isArray(operation.servers)) { servers = operation.servers; } else if (Array.isArray(path.servers)) { servers = path.servers; - this.context = 'path'; } else if (Array.isArray(this.document.servers)) { servers = this.document.servers; - this.context = 'service'; } else { return []; } return servers.map(translateToServer, this).filter(isNonNullable); -}); +}; -export const translateToServer = withContext< - Oas3TranslateFunction, Optional> ->(function (server) { +export const translateToServer: Oas3TranslateFunction, Optional> = function ( + server, +) { if (!isServerObject(server)) return; const variables = translateServerVariables.call(this, server.variables); return { - id: this.generateId(`http_server-${this.parentId}-${server.url}`), url: server.url, ...pickBy( @@ -53,7 +50,7 @@ export const translateToServer = withContext< isNonNullable, ), }; -}); +}; export const translateServerVariables: Oas3TranslateFunction< [variables: unknown], diff --git a/src/postman/__tests__/operation.test.ts b/src/postman/__tests__/operation.test.ts index f003a330..27238491 100644 --- a/src/postman/__tests__/operation.test.ts +++ b/src/postman/__tests__/operation.test.ts @@ -94,9 +94,7 @@ describe('transformPostmanCollectionOperation()', () => { }), ).toEqual( expect.objectContaining({ - security: [ - [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'http-0', scheme: 'basic', type: 'http' }], - ], + security: [[{ key: 'http-0', scheme: 'basic', type: 'http' }]], }), ); }); @@ -125,7 +123,6 @@ describe('transformPostmanCollectionOperation()', () => { request: expect.objectContaining({ headers: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), description: 'Hawk Authorization Header', name: 'Authorization', required: true, @@ -169,15 +166,7 @@ describe('transformPostmanCollectionOperation()', () => { ).toEqual( expect.objectContaining({ request: expect.objectContaining({ - query: [ - { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - description: 'OAuth2 Access Token', - name: 'access_token', - required: true, - style: 'form', - }, - ], + query: [{ description: 'OAuth2 Access Token', name: 'access_token', required: true, style: 'form' }], }), }), ); @@ -234,7 +223,7 @@ describe('transformPostmanCollectionOperation()', () => { }), ).toEqual( expect.objectContaining({ - servers: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), url: 'https://example.com:666' }], + servers: [{ url: 'https://example.com:666' }], }), ); }); @@ -271,20 +260,9 @@ describe('transformPostmanCollectionOperation()', () => { ).toEqual( expect.objectContaining({ request: expect.objectContaining({ - body: { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - contents: [ - { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [ - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'testTESTtest' }, - ], - mediaType: 'text/plain', - }, - ], - }, + body: { contents: [{ examples: [{ key: 'default', value: 'testTESTtest' }], mediaType: 'text/plain' }] }, }), - servers: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), url: 'https://example.com' }], + servers: [{ url: 'https://example.com' }], }), ); }); @@ -353,10 +331,8 @@ describe('transformPostmanCollectionOperations()', () => { request: expect.objectContaining({ headers: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'a header', }, @@ -369,10 +345,8 @@ describe('transformPostmanCollectionOperations()', () => { required: false, }, { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'b header', }, diff --git a/src/postman/__tests__/service.test.ts b/src/postman/__tests__/service.test.ts index 6d111dbf..2f67d4f2 100644 --- a/src/postman/__tests__/service.test.ts +++ b/src/postman/__tests__/service.test.ts @@ -30,7 +30,6 @@ describe('transformPostmanCollectionService()', () => { id: expect.any(String), securitySchemes: [ { - id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', diff --git a/src/postman/__tests__/util.test.ts b/src/postman/__tests__/util.test.ts index 2b5ae3c6..43408104 100644 --- a/src/postman/__tests__/util.test.ts +++ b/src/postman/__tests__/util.test.ts @@ -6,7 +6,7 @@ describe('transformStringValueToSchema()', () => { it('returns param with schema and example', () => { expect(transformStringValueToSchema('test')).toEqual({ schema: { type: 'string' }, - examples: [{ id: expect.any(String), key: 'default', value: 'test' }], + examples: [{ key: 'default', value: 'test' }], }); }); }); diff --git a/src/postman/id.ts b/src/postman/id.ts deleted file mode 100644 index e39b995f..00000000 --- a/src/postman/id.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function generateId() { - return ( - '_gen_' + - Math.round(Math.pow(8, 6) * Math.random()) - .toString(16) - .padStart(6, '0') - ); -} diff --git a/src/postman/operation.ts b/src/postman/operation.ts index e70106d6..57ac6653 100644 --- a/src/postman/operation.ts +++ b/src/postman/operation.ts @@ -1,5 +1,5 @@ -import type { HttpSecurityScheme, IHttpOperation } from '@stoplight/types'; -import type { Collection, CollectionDefinition, Item, RequestAuth, Url } from 'postman-collection'; +import { HttpSecurityScheme, IHttpOperation } from '@stoplight/types'; +import { Collection, CollectionDefinition, Item, RequestAuth, Url } from 'postman-collection'; import { mergeOperations, mergeResponses } from '../merge'; import { transformRequest } from './transformers/request'; @@ -11,7 +11,7 @@ import { transformSecuritySchemes, } from './transformers/securityScheme'; import { transformServer } from './transformers/server'; -import type { PostmanCollectionHttpOperationTransformer } from './types'; +import { PostmanCollectionHttpOperationTransformer } from './types'; import { resolveCollection, transformDescriptionDefinition } from './util'; export const transformPostmanCollectionOperations = (document: CollectionDefinition): IHttpOperation[] => { diff --git a/src/postman/service.ts b/src/postman/service.ts index 4a781b3d..b2e3888a 100644 --- a/src/postman/service.ts +++ b/src/postman/service.ts @@ -1,6 +1,6 @@ -import type { CollectionDefinition } from 'postman-collection'; +import { CollectionDefinition } from 'postman-collection'; -import type { HttpServiceTransformer } from '../types'; +import { HttpServiceTransformer } from '../types'; import { isStandardSecurityScheme, transformSecuritySchemes } from './transformers/securityScheme'; import { resolveCollection, resolveVersion, transformDescriptionDefinition } from './util'; diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index 006de934..fd51a78c 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -7,10 +7,8 @@ describe('transformQueryParam()', () => { describe('value is set', () => { it('transforms correctly', () => { expect(transformQueryParam(new QueryParam({ key: 'testKey', value: 'testValue' }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'testValue', }, @@ -28,7 +26,6 @@ describe('transformQueryParam()', () => { describe('value is null', () => { it('transforms correctly', () => { expect(transformQueryParam(new QueryParam({ key: 'testKey', value: null }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testKey', style: 'form', required: true, @@ -39,7 +36,6 @@ describe('transformQueryParam()', () => { describe('key is null', () => { it('transforms correctly with key being empty string', () => { expect(transformQueryParam(new QueryParam({ key: null, value: null }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: '', style: 'form', required: true, @@ -52,7 +48,6 @@ describe('transformHeader()', () => { describe('value is defined', () => { it('result contains schema', () => { expect(transformHeader(new Header({ key: 'testKey', value: 'some string' }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testkey', schema: { type: 'string', @@ -61,7 +56,6 @@ describe('transformHeader()', () => { required: true, examples: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'some string', }, @@ -73,7 +67,6 @@ describe('transformHeader()', () => { describe('value is not defined', () => { it('results does not contain schema', () => { expect(transformHeader(new Header({ key: 'testKey' }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testkey', style: 'simple', required: true, @@ -85,8 +78,8 @@ describe('transformHeader()', () => { describe('transformPathParams()', () => { it('transforms correctly', () => { expect(transformPathParams(['elem1', ':param1', ':param2', 'elem2'])).toEqual([ - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'param1', style: 'simple', required: true }, - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'param2', style: 'simple', required: true }, + { name: 'param1', style: 'simple', required: true }, + { name: 'param2', style: 'simple', required: true }, ]); }); }); @@ -98,11 +91,9 @@ describe('transformBody()', () => { describe('body is correctly defined json', () => { it('returns body containing example, schema and media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '{"a":"b"}' }), 'application/nice+json')).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { a: 'b' } }], + examples: [{ key: 'default', value: { a: 'b' } }], mediaType: 'application/nice+json', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -125,11 +116,9 @@ describe('transformBody()', () => { describe('body is not a correct JSON', () => { it('returns body containing example and media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '"a":"b"' }), 'application/json')).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: '"a":"b"' }], + examples: [{ key: 'default', value: '"a":"b"' }], mediaType: 'application/json', }, ], @@ -142,11 +131,9 @@ describe('transformBody()', () => { describe('media type is defined', () => { it('returns body containing example and given media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '' }), 'application/xml')).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: '' }], + examples: [{ key: 'default', value: '' }], mediaType: 'application/xml', }, ], @@ -157,13 +144,9 @@ describe('transformBody()', () => { describe('media type is not defined', () => { it('returns body containing example and text/plain media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: "I'm a goat. Bleeet!" }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [ - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: "I'm a goat. Bleeet!" }, - ], + examples: [{ key: 'default', value: "I'm a goat. Bleeet!" }], mediaType: 'text/plain', }, ], @@ -196,13 +179,9 @@ describe('transformBody()', () => { 'multipart/test+form-data', ), ).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [ - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1', k2: 'v2' } }, - ], + examples: [{ key: 'default', value: { k1: 'v1', k2: 'v2' } }], mediaType: 'multipart/test+form-data', schema: { type: 'object', @@ -227,11 +206,9 @@ describe('transformBody()', () => { }), ), ).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1' } }], + examples: [{ key: 'default', value: { k1: 'v1' } }], mediaType: 'multipart/form-data', schema: { type: 'object', @@ -257,13 +234,9 @@ describe('transformBody()', () => { ); expect(result).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [ - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: expect.any(Object) }, - ], + examples: [{ key: 'default', value: expect.any(Object) }], mediaType: 'multipart/test+form-data', schema: { type: 'object', @@ -299,11 +272,9 @@ describe('transformBody()', () => { describe('body is not defined', () => { it('returns default empty body', () => { expect(transformBody(new RequestBody({ mode: 'formdata' }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: {} }], + examples: [{ key: 'default', value: {} }], mediaType: 'multipart/form-data', schema: { properties: {}, type: 'object' }, }, @@ -329,13 +300,9 @@ describe('transformBody()', () => { 'application/test+x-www-form-urlencoded', ), ).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [ - { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1', k2: 'v2' } }, - ], + examples: [{ key: 'default', value: { k1: 'v1', k2: 'v2' } }], mediaType: 'application/test+x-www-form-urlencoded', schema: { type: 'object', @@ -360,11 +327,9 @@ describe('transformBody()', () => { }), ), ).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1' } }], + examples: [{ key: 'default', value: { k1: 'v1' } }], mediaType: 'application/x-www-form-urlencoded', schema: { type: 'object', @@ -382,11 +347,9 @@ describe('transformBody()', () => { describe('body is not defined', () => { it('returns default empty body', () => { expect(transformBody(new RequestBody({ mode: 'urlencoded' }))).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), - examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: {} }], + examples: [{ key: 'default', value: {} }], mediaType: 'application/x-www-form-urlencoded', schema: { properties: {}, type: 'object' }, }, diff --git a/src/postman/transformers/__tests__/request.test.ts b/src/postman/transformers/__tests__/request.test.ts index 27b782b9..679d0da3 100644 --- a/src/postman/transformers/__tests__/request.test.ts +++ b/src/postman/transformers/__tests__/request.test.ts @@ -15,30 +15,26 @@ describe('transformRequest()', () => { ), ).toEqual({ body: { - id: expect.any(String), contents: [ { - id: expect.any(String), - examples: [{ id: expect.any(String), key: 'default', value: 'test' }], + examples: [{ key: 'default', value: 'test' }], mediaType: 'text/plain', }, ], }, headers: [ { - id: expect.any(String), name: 'header', schema: { type: 'string' }, - examples: [{ id: expect.any(String), key: 'default', value: 'a header' }], + examples: [{ key: 'default', value: 'a header' }], style: 'simple', required: true, }, ], - path: [{ id: expect.any(String), name: 'param', style: 'simple', required: true }], + path: [{ name: 'param', style: 'simple', required: true }], query: [ { - id: expect.any(String), - examples: [{ id: expect.any(String), key: 'default', value: 'b' }], + examples: [{ key: 'default', value: 'b' }], schema: { type: 'string' }, name: 'a', style: 'form', diff --git a/src/postman/transformers/__tests__/response.test.ts b/src/postman/transformers/__tests__/response.test.ts index dc502b9a..93cb7ea4 100644 --- a/src/postman/transformers/__tests__/response.test.ts +++ b/src/postman/transformers/__tests__/response.test.ts @@ -23,14 +23,11 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), code: '200', contents: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { - id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { "I'm a JSON": 'Jieeeet!', @@ -54,10 +51,8 @@ describe('transformResponse()', () => { ], headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'application/json', }, @@ -70,10 +65,8 @@ describe('transformResponse()', () => { style: 'simple', }, { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'eat=functions; Domain=.example.com; Path=/', }, @@ -97,7 +90,6 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', headers: [], }); @@ -116,7 +108,6 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', description: 'Test', headers: [], @@ -147,14 +138,11 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'eat=functions; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=300; Domain=example.com; Path=/; Secure; HttpOnly', @@ -186,14 +174,11 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'eat=functions; Domain=example.com; Path=/', }, @@ -222,14 +207,11 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: '=; Domain=example.com; Path=/', }, @@ -264,14 +246,11 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'eat=functions; Domain=example.com; Path=/; a=p; u=d', }, @@ -303,14 +282,11 @@ describe('transformResponse()', () => { }), ), ).toEqual({ - id: expect.any(String), code: '200', headers: [ { - id: expect.any(String), examples: [ { - id: expect.any(String), key: 'default', value: 'eat=functions; Expires=Fri, 11 Aug 2017 09:04:08 GMT; Domain=example.com; Path=/', }, diff --git a/src/postman/transformers/__tests__/securitySchemes.spec.ts b/src/postman/transformers/__tests__/securitySchemes.spec.ts index 396c4fa4..13e0828a 100644 --- a/src/postman/transformers/__tests__/securitySchemes.spec.ts +++ b/src/postman/transformers/__tests__/securitySchemes.spec.ts @@ -33,7 +33,6 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { - id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'basic', @@ -71,7 +70,6 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { - id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'digest', @@ -99,7 +97,6 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { - id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'bearer', @@ -138,7 +135,6 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { - id: expect.any(String), key: 'auth-apiKey', type: 'apiKey', name: 'TestApiKey', @@ -172,7 +168,6 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { - id: expect.any(String), key: 'auth-apiKey', type: 'apiKey', name: 'TestApiKey', @@ -213,14 +208,12 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { - id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { - id: expect.any(String), key: 'default', value: 'OAuth realm="karol@stoplight.io",oauth_consumer_key="a_consumer_key",oauth_token="a_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="123123123123",oauth_nonce="a",oauth_version="1.0",oauth_signature="a_signature"', @@ -248,14 +241,12 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { - id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { - id: expect.any(String), key: 'default', value: 'OAuth realm="a_realm",oauth_consumer_key="a_consumer_key",oauth_token="a_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="0",oauth_nonce="a",oauth_version="1.0",oauth_signature="a_signature"', @@ -283,32 +274,29 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, - { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_token', style: HttpParamStyles.Form, required: false }, { - id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: false, - examples: [{ id: expect.any(String), key: 'default', value: 'HMAC-SHA1' }], + examples: [{ key: 'default', value: 'HMAC-SHA1' }], }, { - id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: false, schema: { type: 'string' }, - examples: [{ id: expect.any(String), key: 'default', value: '123123123123' }], + examples: [{ key: 'default', value: '123123123123' }], }, - { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, { - id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: false, - examples: [{ id: expect.any(String), key: 'default', value: '1.0' }], + examples: [{ key: 'default', value: '1.0' }], }, - { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, ], }); }); @@ -329,32 +317,29 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, - { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_token', style: HttpParamStyles.Form, required: false }, { - id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: false, examples: [], }, { - id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: false, schema: { type: 'string' }, examples: [], }, - { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, { - id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: false, examples: [], }, - { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, + { name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, ], }); }); @@ -374,32 +359,29 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: true }, - { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: true }, + { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: true }, + { name: 'oauth_token', style: HttpParamStyles.Form, required: true }, { - id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: true, - examples: [{ id: expect.any(String), key: 'default', value: 'HMAC-SHA1' }], + examples: [{ key: 'default', value: 'HMAC-SHA1' }], }, { - id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: true, schema: { type: 'string' }, - examples: [{ id: expect.any(String), key: 'default', value: '123123123123' }], + examples: [{ key: 'default', value: '123123123123' }], }, - { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: true }, + { name: 'oauth_nonce', style: HttpParamStyles.Form, required: true }, { - id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: true, - examples: [{ id: expect.any(String), key: 'default', value: '1.0' }], + examples: [{ key: 'default', value: '1.0' }], }, - { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: true }, + { name: 'oauth_signature', style: HttpParamStyles.Form, required: true }, ], }); }); @@ -432,7 +414,6 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { - id: expect.any(String), key: 'auth-oauth2', description: 'OAuth2 Access Token', scheme: 'bearer', @@ -467,7 +448,6 @@ describe('transformSecurityScheme()', () => { type: 'queryParams', queryParams: [ { - id: expect.any(String), name: 'access_token', style: HttpParamStyles.Form, description: 'OAuth2 Access Token', @@ -503,7 +483,6 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { - id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'Hawk Authorization Header', @@ -538,19 +517,16 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { - id: expect.any(String), name: 'X-Amz-Security-Token', style: HttpParamStyles.Simple, required: true, }, { - id: expect.any(String), name: 'X-Amz-Date', style: HttpParamStyles.Simple, required: true, }, { - id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -583,7 +559,6 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { - id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'Akamai EdgeGrid Authorization Header', @@ -613,7 +588,6 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { - id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'NTLM Authorization Header', @@ -673,7 +647,6 @@ describe('transformPostmanSecuritySchemes()', () => { ).toEqual([ { securityScheme: { - id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', @@ -682,7 +655,6 @@ describe('transformPostmanSecuritySchemes()', () => { }, { securityScheme: { - id: expect.any(String), key: 'http-1', scheme: 'digest', type: 'http', @@ -781,7 +753,6 @@ describe('transformPostmanSecuritySchemes()', () => { ).toEqual([ { securityScheme: { - id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', @@ -796,26 +767,26 @@ describe('transformPostmanSecuritySchemes()', () => { describe.each<[string, PostmanSecurityScheme, PostmanSecurityScheme, boolean]>([ [ 'two equal security schemes', - { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, - { type: 'securityScheme', securityScheme: { id: '', key: '2', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { key: '2', type: 'http', scheme: 'basic' } }, true, ], [ 'different types', - { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, { type: 'headerParams', headerParams: [] }, false, ], [ 'two different', - { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, - { type: 'securityScheme', securityScheme: { id: '', key: '2', type: 'http', scheme: 'digest' } }, + { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { key: '2', type: 'http', scheme: 'digest' } }, false, ], [ 'two equal query params', - { type: 'queryParams', queryParams: [{ id: '', name: 'token', style: HttpParamStyles.Form }] }, - { type: 'queryParams', queryParams: [{ id: '', name: 'token', style: HttpParamStyles.Form }] }, + { type: 'queryParams', queryParams: [{ name: 'token', style: HttpParamStyles.Form }] }, + { type: 'queryParams', queryParams: [{ name: 'token', style: HttpParamStyles.Form }] }, true, ], ])('given %s security schemes', (desc, scheme1, scheme2, result) => { diff --git a/src/postman/transformers/__tests__/server.test.ts b/src/postman/transformers/__tests__/server.test.ts index 4f623f42..4410a412 100644 --- a/src/postman/transformers/__tests__/server.test.ts +++ b/src/postman/transformers/__tests__/server.test.ts @@ -6,19 +6,13 @@ describe('transformServer()', () => { describe('host is defined', () => { describe('port is defined', () => { it('produces server with port', () => { - expect(transformServer(new Url('https://example.com:666/path'))).toEqual({ - id: expect.any(String), - url: 'https://example.com:666', - }); + expect(transformServer(new Url('https://example.com:666/path'))).toEqual({ url: 'https://example.com:666' }); }); }); describe('port is not defined', () => { it('produces server without port', () => { - expect(transformServer(new Url('https://example.com/path'))).toEqual({ - id: expect.any(String), - url: 'https://example.com', - }); + expect(transformServer(new Url('https://example.com/path'))).toEqual({ url: 'https://example.com' }); }); }); }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 96370a5c..341e8fa4 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -9,16 +9,14 @@ import { import type { JSONSchema7 } from 'json-schema'; // @ts-ignore import * as jsonSchemaGenerator from 'json-schema-generator'; -import type { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; +import { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; import { convertSchema } from '../../oas/transformers/schema'; -import { generateId } from '../id'; import { transformDescriptionDefinition, transformStringValueToSchema } from '../util'; export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { return { - id: generateId(), name: queryParam.key || '', style: HttpParamStyles.Form, required: true, @@ -28,7 +26,6 @@ export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { export function transformHeader(header: Header): IHttpHeaderParam { return { - id: generateId(), name: header.key.toLowerCase(), style: HttpParamStyles.Simple, required: true, @@ -40,7 +37,6 @@ export function transformPathParams(segments: string[]): IHttpPathParam[] { return segments.reduce((params, segment) => { if (segment.startsWith(':')) { params.push({ - id: generateId(), name: segment.substring(1), style: HttpParamStyles.Simple, required: true, @@ -55,19 +51,17 @@ export function transformBody(body: RequestBody, mediaType?: string): IHttpOpera switch (body.mode) { case 'raw': if (!body.raw) return; - return { id: generateId(), contents: [transformRawBody(body.raw, mediaType)] }; + return { contents: [transformRawBody(body.raw, mediaType)] }; case 'formdata': if (!body.formdata) return; return { - id: generateId(), contents: [transformParamsBody(body.formdata, mediaType || 'multipart/form-data')], }; case 'urlencoded': if (!body.urlencoded) return; return { - id: generateId(), contents: [transformParamsBody(body.urlencoded, mediaType || 'application/x-www-form-urlencoded')], }; } @@ -81,11 +75,9 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): const parsed = JSON.parse(raw); return { - id: generateId(), mediaType, examples: [ { - id: generateId(), key: 'default', value: parsed, }, @@ -98,11 +90,9 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): } return { - id: generateId(), mediaType, examples: [ { - id: generateId(), key: 'default', value: raw, }, @@ -126,7 +116,6 @@ function transformParamsBody( }); return { - id: generateId(), mediaType, schema: { type: 'object', @@ -137,7 +126,6 @@ function transformParamsBody( }, examples: [ { - id: generateId(), key: 'default', value: paramsList.reduce((values, param) => { values[param.name] = param.value; @@ -147,3 +135,12 @@ function transformParamsBody( ], }; } + +function generateId() { + return ( + '_gen_' + + Math.round(Math.pow(8, 6) * Math.random()) + .toString(16) + .padStart(6, '0') + ); +} diff --git a/src/postman/transformers/response.ts b/src/postman/transformers/response.ts index a2efe60c..97f8b350 100644 --- a/src/postman/transformers/response.ts +++ b/src/postman/transformers/response.ts @@ -1,7 +1,6 @@ import { HttpParamStyles, IHttpHeaderParam, IHttpOperationResponse } from '@stoplight/types'; import { Cookie, Response } from 'postman-collection'; -import { generateId } from '../id'; import { transformDescriptionDefinition } from '../util'; import { transformHeader, transformRawBody } from './params'; @@ -10,7 +9,6 @@ export function transformResponse(response: Response): IHttpOperationResponse { const mediaType = response.headers.get('content-type'); return { - id: generateId(), code: String(response.code), description: response.description && transformDescriptionDefinition(response.description), headers: headers.concat(response.cookies.map(transformCookie).filter((c: Cookie) => c)), @@ -31,11 +29,9 @@ function transformCookie(cookie: Cookie): IHttpHeaderParam | undefined { if (cookie.extensions) params.push(...cookie.extensions.map(({ key, value }) => `${key}=${value}`)); return { - id: generateId(), name: 'set-cookie', examples: [ { - id: generateId(), key: 'default', value: params.join('; '), }, diff --git a/src/postman/transformers/securityScheme.ts b/src/postman/transformers/securityScheme.ts index ba48f408..816b2cb3 100644 --- a/src/postman/transformers/securityScheme.ts +++ b/src/postman/transformers/securityScheme.ts @@ -1,8 +1,6 @@ import { HttpParamStyles, HttpSecurityScheme, IHttpHeaderParam, IHttpQueryParam } from '@stoplight/types'; -import type { Collection, RequestAuth } from 'postman-collection'; - -import { isEqual } from '../../utils'; -import { generateId } from '../id'; +import { Collection, RequestAuth } from 'postman-collection'; +import isEqual = require('lodash.isequal'); export type PostmanSecurityScheme = StandardSecurityScheme | QuerySecurityScheme | HeaderSecurityScheme; @@ -38,14 +36,12 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { - id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { - id: generateId(), key: 'default', value: 'OAuth ' + @@ -73,38 +69,31 @@ export function transformSecurityScheme( return { type: 'queryParams', queryParams: [ - { id: generateId(), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required }, - { id: generateId(), name: 'oauth_token', style: HttpParamStyles.Form, required }, + { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required }, + { name: 'oauth_token', style: HttpParamStyles.Form, required }, { - id: generateId(), name: 'oauth_signature_method', style: HttpParamStyles.Form, required, examples: parameters.has('signatureMethod') - ? [{ id: generateId(), key: 'default', value: parameters.get('signatureMethod') }] + ? [{ key: 'default', value: parameters.get('signatureMethod') }] : [], }, { - id: generateId(), name: 'oauth_timestamp', style: HttpParamStyles.Form, required, schema: { type: 'string' }, - examples: parameters.has('timestamp') - ? [{ id: generateId(), key: 'default', value: parameters.get('timestamp') }] - : [], + examples: parameters.has('timestamp') ? [{ key: 'default', value: parameters.get('timestamp') }] : [], }, - { id: generateId(), name: 'oauth_nonce', style: HttpParamStyles.Form, required }, + { name: 'oauth_nonce', style: HttpParamStyles.Form, required }, { - id: generateId(), name: 'oauth_version', style: HttpParamStyles.Form, required, - examples: parameters.has('version') - ? [{ id: generateId(), key: 'default', value: parameters.get('version') }] - : [], + examples: parameters.has('version') ? [{ key: 'default', value: parameters.get('version') }] : [], }, - { id: generateId(), name: 'oauth_signature', style: HttpParamStyles.Form, required }, + { name: 'oauth_signature', style: HttpParamStyles.Form, required }, ], }; } @@ -114,7 +103,6 @@ export function transformSecurityScheme( type: 'queryParams', queryParams: [ { - id: generateId(), name: 'access_token', description: 'OAuth2 Access Token', style: HttpParamStyles.Form, @@ -126,7 +114,6 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { - id: generateId(), key: nextKey('oauth2'), type: 'http', scheme: 'bearer', @@ -139,7 +126,6 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { - id: generateId(), key: nextKey('apiKey'), type: 'apiKey', name: parameters.get('key'), @@ -153,7 +139,6 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { - id: generateId(), key: nextKey('http'), type: 'http', scheme: auth.type, @@ -165,7 +150,6 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { - id: generateId(), name: 'Authorization', description: 'Hawk Authorization Header', required: true, @@ -183,19 +167,16 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { - id: generateId(), name: 'X-Amz-Security-Token', style: HttpParamStyles.Simple, required: true, }, { - id: generateId(), name: 'X-Amz-Date', style: HttpParamStyles.Simple, required: true, }, { - id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -209,7 +190,6 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { - id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -223,7 +203,6 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { - id: generateId(), name: 'Authorization', description: 'NTLM Authorization Header', required: true, @@ -248,7 +227,9 @@ export function isPostmanSecuritySchemeEqual(pss1: PostmanSecurityScheme, pss2: if (pss1.type !== pss2.type) return false; if (isStandardSecurityScheme(pss1) && isStandardSecurityScheme(pss2)) { - return isEqual({ ...pss1.securityScheme, key: '' }, { ...pss2.securityScheme, key: '' }); + const { key: _key, ...pss1SecurityScheme } = pss1.securityScheme; + const { key: _key2, ...pss2SecurityScheme } = pss2.securityScheme; + return isEqual(pss1SecurityScheme, pss2SecurityScheme); } return isEqual(pss1, pss2); diff --git a/src/postman/transformers/server.ts b/src/postman/transformers/server.ts index ccda6fd3..e2898163 100644 --- a/src/postman/transformers/server.ts +++ b/src/postman/transformers/server.ts @@ -1,12 +1,10 @@ -import type { IServer } from '@stoplight/types'; -import type { Url } from 'postman-collection'; - -import { generateId } from '../id'; +import { IServer } from '@stoplight/types'; +import { Url } from 'postman-collection'; export function transformServer(url: Url): IServer | undefined { try { const origin = new URL(url.toString()).origin; - return origin ? { id: generateId(), url: origin } : undefined; + return origin ? { url: origin } : undefined; } catch { return undefined; } diff --git a/src/postman/types.ts b/src/postman/types.ts index 350c7555..0503aeff 100644 --- a/src/postman/types.ts +++ b/src/postman/types.ts @@ -1,6 +1,6 @@ -import type { CollectionDefinition } from 'postman-collection'; +import { CollectionDefinition } from 'postman-collection'; -import type { HttpOperationTransformer } from '../types'; +import { HttpOperationTransformer } from '../types'; export type PostmanCollectionHttpOperationTransformer = HttpOperationTransformer<{ document: CollectionDefinition; diff --git a/src/postman/util.ts b/src/postman/util.ts index ca5277f6..8c8e321a 100644 --- a/src/postman/util.ts +++ b/src/postman/util.ts @@ -1,13 +1,10 @@ -import type { IHttpParam } from '@stoplight/types'; +import { IHttpParam } from '@stoplight/types'; import { Collection, CollectionDefinition, DescriptionDefinition, Version } from 'postman-collection'; -import { generateId } from './id'; - export function transformStringValueToSchema(value: string): Pick { return { examples: [ { - id: generateId(), key: 'default', value, }, diff --git a/src/track.ts b/src/track.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/types.ts b/src/types.ts index ae632ad8..5ec95eff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,13 +2,6 @@ import type { IHttpOperation, IHttpService } from '@stoplight/types'; export type Fragment = Record; -export type IdGenerator = (value: string) => string; - -export type RefResolver = ( - this: TransformerContext, - input: Fragment & { $ref: string }, -) => unknown; - export interface ITransformServiceOpts { document: T; } @@ -23,20 +16,14 @@ export interface ITransformOperationOpts { export type HttpOperationTransformer = (opts: T) => IHttpOperation; -export type ArrayCallbackParameters = [T, number, T[]]; - -export type AvailableContext = 'service' | 'path' | 'operation'; - export type TransformerContext = { - document: T; - context: AvailableContext; - parentId: string; - readonly ids: Record; - generateId(template: string): string; maybeResolveLocalRef(target: unknown): unknown; + document: T; }; export type TranslateFunction = ( this: TransformerContext, ...params: P ) => R; + +export type ArrayCallbackParameters = [T, number, T[]]; diff --git a/src/utils.ts b/src/utils.ts index 39c3dee5..3d177946 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,15 +1,21 @@ -import { isPlainObject } from '@stoplight/json'; -import isEqualWith = require('lodash.isequalwith'); +import { hasRef, isLocalRef, isPlainObject, resolveInlineRef } from '@stoplight/json'; + +export const maybeResolveLocalRef = (document: unknown, target: unknown): unknown => { + if (isPlainObject(document) && hasRef(target) && isLocalRef(target.$ref)) { + try { + return resolveInlineRef(document, target.$ref); + } catch { + return target; + } + } + + return target; +}; + +export { isPlainObject as isDictionary }; export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; export function entries(o: T): [string, T][]; export function entries(o: T): [string, T][] { return isPlainObject(o) ? Object.entries(o as T) : []; } - -export function isEqual(left: unknown, right: unknown) { - return isEqualWith(left, right, (value, other, indexOrKey) => { - if (indexOrKey === 'id') return true; - return; - }); -} diff --git a/yarn.lock b/yarn.lock index beb5000d..88d5e7d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== - dependencies: - "@babel/highlight" "^7.10.1" - -"@babel/code-frame@^7.10.4": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== @@ -75,12 +68,7 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" - integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== - -"@babel/helper-validator-identifier@^7.16.7": +"@babel/helper-validator-identifier@^7.10.1", "@babel/helper-validator-identifier@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== @@ -94,25 +82,16 @@ "@babel/traverse" "^7.8.4" "@babel/types" "^7.8.3" -"@babel/highlight@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.1.tgz#841d098ba613ba1a427a2b383d79e35552c38ae0" - integrity sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg== - dependencies: - "@babel/helper-validator-identifier" "^7.10.1" - chalk "^2.0.0" - js-tokens "^4.0.0" - "@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": +"@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== @@ -187,14 +166,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/runtime@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" - integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": +"@babel/template@^7.3.3", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" integrity sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig== @@ -203,7 +175,7 @@ "@babel/parser" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== @@ -1110,17 +1082,17 @@ integrity sha512-Cj3waLFR9bYLG8yvgkjXMxOfiVdewRyrKdH5RQpttVNfKj5UF+mElofPcHn/UfiOuxK3t5rbsdMfS26LK5tdQg== "@stoplight/types@^12.3.0": - version "12.5.0" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.5.0.tgz#ebbeeb8c874de30e4cd9a1a2a6c8d6062c155da0" - integrity sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg== + version "12.4.0" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.4.0.tgz#7a90e747d3f531742316b7b2969e6c45efd3e968" + integrity sha512-ELf0dOdROaKEE0iw1YF9qbL9QjIVfdyiCoBPpUQmvQej9F7tQR/TsYMvyUb8kz+rd49u3hGP/sirVuaTKI1ZOg== dependencies: "@types/json-schema" "^7.0.4" utility-types "^3.10.0" -"@stoplight/types@^13.0.0-beta.5": - version "13.0.0-beta.5" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.5.tgz#31804f3d1e6369a6daeff985dd0ca7ab27637f70" - integrity sha512-qZgWB3QxoOJL2TsbSfPSyStsnH7BwvAljcT2xa0ZowP3/Eh16OYS/PWzQzTU/GZ+btyqpowGgTragLUgipQAjQ== +"@stoplight/types@^13.0.0-beta.4": + version "13.0.0-beta.4" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.4.tgz#5ea5e93957b20a5effedcbd101e6e9cde7118e48" + integrity sha512-q9Dbyfi1DcZjTuDwptVLTCbeQgtpfTQAKQkbcrs64ayfvc7dPwO+81vYr0dSJsXzlFQktZv5M+eq0QPtJy/HGg== dependencies: "@types/json-schema" "^7.0.4" utility-types "^3.10.0" @@ -1130,7 +1102,7 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" integrity sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA== -"@types/babel__core@^7.0.0": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.9" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== @@ -1141,17 +1113,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.7": - version "7.1.8" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.8.tgz#057f725aca3641f49fc11c7a87a9de5ec588a5d7" - integrity sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__generator@*": version "7.6.0" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.0.tgz#f1ec1c104d1bb463556ecb724018ab788d0c172a" @@ -1224,12 +1185,12 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/json-schema@7.0.5", "@types/json-schema@^7.0.4": +"@types/json-schema@7.0.5": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== -"@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.9": version "7.0.10" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.10.tgz#9b05b7896166cd00e9cbd59864853abf65d9ac23" integrity sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A== @@ -1239,10 +1200,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash.isequalwith@^4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@types/lodash.isequalwith/-/lodash.isequalwith-4.4.6.tgz#8c5dee2b2fdc05cfa79a3a77cd40cc8909ef79c9" - integrity sha512-55fjBOrhse+SLkqGlvUq1yQ/sDvsi93+ngLIG22DPEoeL4c/d1rtWMMK55pP4nKOjkSWv8ks1q0HaXXCgPr81w== +"@types/lodash.isequal@^4.5.5": + version "4.5.5" + resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz#4fed1b1b00bef79e305de0352d797e9bb816c8ff" + integrity sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg== dependencies: "@types/lodash" "*" @@ -1265,11 +1226,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== -"@types/memoizee@^0.4.7": - version "0.4.7" - resolved "https://registry.yarnpkg.com/@types/memoizee/-/memoizee-0.4.7.tgz#fa0025b5ea1c8acb85fd52a76982c5a096c2c91e" - integrity sha512-EMtvWnRC/W0KYmXxbPJAMg/M1OXkFboSpd/IzkNMDXfoFPE4uom1RHFl8q7m/4R0y9eG1yaoaIPiMHk0vMA0eA== - "@types/node@*", "@types/node@>= 8": version "13.9.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.1.tgz#96f606f8cd67fb018847d9b61e93997dabdefc72" @@ -1339,13 +1295,13 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz#c28ef7f2e688066db0b6a9d95fb74185c114fb9a" - integrity sha512-u6Db5JfF0Esn3tiAKELvoU5TpXVSkOpZ78cEGn/wXtT2RVqs2vkt4ge6N8cRCyw7YVKhmmLDbwI2pg92mlv7cA== + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" + integrity sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw== dependencies: - "@typescript-eslint/scope-manager" "5.15.0" - "@typescript-eslint/type-utils" "5.15.0" - "@typescript-eslint/utils" "5.15.0" + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/type-utils" "5.16.0" + "@typescript-eslint/utils" "5.16.0" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -1354,68 +1310,68 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.15.0.tgz#95f603f8fe6eca7952a99bfeef9b85992972e728" - integrity sha512-NGAYP/+RDM2sVfmKiKOCgJYPstAO40vPAgACoWPO/+yoYKSgAXIFaBKsV8P0Cc7fwKgvj27SjRNX4L7f4/jCKQ== + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.16.0.tgz#e4de1bde4b4dad5b6124d3da227347616ed55508" + integrity sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA== dependencies: - "@typescript-eslint/scope-manager" "5.15.0" - "@typescript-eslint/types" "5.15.0" - "@typescript-eslint/typescript-estree" "5.15.0" + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/typescript-estree" "5.16.0" debug "^4.3.2" -"@typescript-eslint/scope-manager@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz#d97afab5e0abf4018d1289bd711be21676cdd0ee" - integrity sha512-EFiZcSKrHh4kWk0pZaa+YNJosvKE50EnmN4IfgjkA3bTHElPtYcd2U37QQkNTqwMCS7LXeDeZzEqnsOH8chjSg== +"@typescript-eslint/scope-manager@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz#7e7909d64bd0c4d8aef629cdc764b9d3e1d3a69a" + integrity sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ== dependencies: - "@typescript-eslint/types" "5.15.0" - "@typescript-eslint/visitor-keys" "5.15.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/visitor-keys" "5.16.0" -"@typescript-eslint/type-utils@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz#d2c02eb2bdf54d0a645ba3a173ceda78346cf248" - integrity sha512-KGeDoEQ7gHieLydujGEFLyLofipe9PIzfvA/41urz4hv+xVxPEbmMQonKSynZ0Ks2xDhJQ4VYjB3DnRiywvKDA== +"@typescript-eslint/type-utils@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz#b482bdde1d7d7c0c7080f7f2f67ea9580b9e0692" + integrity sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ== dependencies: - "@typescript-eslint/utils" "5.15.0" + "@typescript-eslint/utils" "5.16.0" debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/types@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.15.0.tgz#c7bdd103843b1abae97b5518219d3e2a0d79a501" - integrity sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA== +"@typescript-eslint/types@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.16.0.tgz#5827b011982950ed350f075eaecb7f47d3c643ee" + integrity sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g== -"@typescript-eslint/typescript-estree@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz#81513a742a9c657587ad1ddbca88e76c6efb0aac" - integrity sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA== +"@typescript-eslint/typescript-estree@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz#32259459ec62f5feddca66adc695342f30101f61" + integrity sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ== dependencies: - "@typescript-eslint/types" "5.15.0" - "@typescript-eslint/visitor-keys" "5.15.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/visitor-keys" "5.16.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/utils@5.15.0", "@typescript-eslint/utils@^5.10.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.15.0.tgz#468510a0974d3ced8342f37e6c662778c277f136" - integrity sha512-081rWu2IPKOgTOhHUk/QfxuFog8m4wxW43sXNOMSCdh578tGJ1PAaWPsj42LOa7pguh173tNlMigsbrHvh/mtA== +"@typescript-eslint/utils@5.16.0", "@typescript-eslint/utils@^5.10.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.16.0.tgz#42218b459d6d66418a4eb199a382bdc261650679" + integrity sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.15.0" - "@typescript-eslint/types" "5.15.0" - "@typescript-eslint/typescript-estree" "5.15.0" + "@typescript-eslint/scope-manager" "5.16.0" + "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/typescript-estree" "5.16.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz#5669739fbf516df060f978be6a6dce75855a8027" - integrity sha512-+vX5FKtgvyHbmIJdxMJ2jKm9z2BIlXJiuewI8dsDYMp5LzPUcuTT78Ya5iwvQg3VqSVdmxyM8Anj1Jeq7733ZQ== +"@typescript-eslint/visitor-keys@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz#f27dc3b943e6317264c7492e390c6844cd4efbbb" + integrity sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g== dependencies: - "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/types" "5.16.0" eslint-visitor-keys "^3.0.0" JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: @@ -1455,9 +1411,9 @@ acorn-walk@^7.1.1: integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== acorn@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" - integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" @@ -1500,17 +1456,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv@^6.10.0: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.4, ajv@^6.5.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1520,16 +1466,6 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.5.5: - version "6.12.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" - integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" @@ -1564,12 +1500,7 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-regex@^5.0.1: +ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -2801,12 +2732,12 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: - ms "^2.1.1" + ms "2.1.2" debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" @@ -2815,27 +2746,13 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -2891,7 +2808,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.2, define-properties@^1.1.3: +define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -3126,24 +3043,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: - version "1.17.6" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" - integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.0" - is-regex "^1.1.0" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.17.0-next.1, es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -3258,9 +3158,9 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.12.0" eslint-plugin-jest@^26.1.1: - version "26.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.1.tgz#7176dd745ef8bca3070263f62cdf112f2dfc9aa1" - integrity sha512-HRKOuPi5ADhza4ZBK5ufyNXy28bXXkib87w+pQqdvBhSTsamndh6sIAKPAUl8y0/n9jSWBdTPslrwtKWqkp8dA== + version "26.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.2.tgz#0f1a15c62889fffc3f78a773749d672f1bedb15f" + integrity sha512-1bXCoRODPkGN06n9KAMls4Jm0eyS+0Q/LWcIxhqWR2ycV0Z7lnx2c10idk4dtFIJY5xStgiIr5snC6/rxcXpbw== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -3413,12 +3313,7 @@ estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" - integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== - -estraverse@^5.2.0, estraverse@^5.3.0: +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -3610,18 +3505,6 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.1.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" - integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" - merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" - fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -3800,11 +3683,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -fnv-plus@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/fnv-plus/-/fnv-plus-1.3.1.tgz#c34cb4572565434acb08ba257e4044ce2b006d67" - integrity sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw== - for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -3896,12 +3774,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== - -fsevents@~2.3.2: +fsevents@^2.1.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -4048,13 +3921,6 @@ git-raw-commits@^2.0.0: split2 "^2.0.0" through2 "^3.0.0" -glob-parent@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== - dependencies: - is-glob "^4.0.1" - glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -4132,19 +3998,7 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globby@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.0.tgz#56fd0e9f0d4f8fb0c456f1ab0dee96e1380bc154" - integrity sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - -globby@^11.0.4: +globby@^11.0.0, globby@^11.0.4: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -4235,12 +4089,7 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.0, has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has-symbols@^1.0.2: +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -4447,11 +4296,6 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" - integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== - ignore@^5.1.8, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -4465,15 +4309,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-fresh@^3.2.1: +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -4672,12 +4508,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" - integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== - -is-callable@^1.2.4: +is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== @@ -4791,14 +4622,7 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4907,13 +4731,6 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" -is-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" - integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw== - dependencies: - has-symbols "^1.0.1" - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -4947,26 +4764,14 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== - -is-string@^1.0.7: +is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" - -is-symbol@^1.0.3: +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== @@ -5065,20 +4870,7 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" - integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== - dependencies: - "@babel/core" "^7.7.5" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-instrument@^4.0.3: +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== @@ -5477,15 +5269,7 @@ jest-watcher@^26.1.0: jest-util "^26.1.0" string-length "^4.0.1" -jest-worker@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.1.0.tgz#65d5641af74e08ccd561c240e7db61284f82f33d" - integrity sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ== - dependencies: - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^26.2.1: +jest-worker@^26.1.0, jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -6042,10 +5826,10 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.isequalwith@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0" - integrity sha1-Jmcm3dUo+FTyH06pigZWBuD7xrA= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= lodash.ismatch@^4.4.0: version "4.4.0" @@ -6236,14 +6020,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" - integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== - dependencies: - semver "^6.0.0" - -make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -6386,12 +6163,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" - integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== - -merge2@^1.4.1: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -6401,13 +6173,13 @@ merge@^1.2.1: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== -micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== +micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== dependencies: braces "^3.0.1" - picomatch "^2.0.5" + picomatch "^2.2.3" micromatch@^3.0.4, micromatch@^3.1.4: version "3.1.10" @@ -6428,23 +6200,10 @@ micromatch@^3.0.4, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" - -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== - -mime-db@1.49.0: - version "1.49.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" - integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== +mime-db@1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== mime-format@2.0.1: version "2.0.1" @@ -6453,19 +6212,12 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.32: - version "2.1.32" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" - integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== +mime-types@2.1.34, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== dependencies: - mime-db "1.49.0" - -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== - dependencies: - mime-db "1.44.0" + mime-db "1.51.0" mime@^2.4.3: version "2.4.4" @@ -6482,14 +6234,7 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -6509,10 +6254,10 @@ minimist@1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== minimist@~0.0.1: version "0.0.10" @@ -7029,12 +6774,7 @@ object-inspect@^1.11.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== -object-inspect@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -7046,16 +6786,6 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" @@ -7279,20 +7009,13 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" -p-limit@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== - dependencies: - p-try "^2.0.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -7482,12 +7205,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg== -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-parse@^1.0.7: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -7516,12 +7234,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== - -picomatch@^2.2.2, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -7571,9 +7284,9 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postman-collection@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.0.tgz#c5833aa3cb82df79cc5d16e5d7399c71a84ea4fa" - integrity sha512-J9IpCMXpGDLN7MGhdMcUbZ0SIWLCcTVdrjTgKVYubkW1sn1KcDqJgsdTr/ItkO8dOXKLuhvnq2QnE5Vrzb3WMA== + version "4.1.1" + resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.1.tgz#aba50dfca3c3ce6881b9598958c6a52eec3959d9" + integrity sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg== dependencies: faker "5.5.3" file-type "3.9.0" @@ -7582,7 +7295,7 @@ postman-collection@^4.1.0: liquid-json "0.3.1" lodash "4.17.21" mime-format "2.0.1" - mime-types "2.1.32" + mime-types "2.1.34" postman-url-encoder "3.0.5" semver "7.3.5" uuid "8.3.2" @@ -7977,11 +7690,6 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.4: - version "0.13.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -7990,7 +7698,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.1: +regexp.prototype.flags@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== @@ -8148,14 +7856,7 @@ resolve@1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -resolve@^1.19.0, resolve@^1.20.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -8401,18 +8102,13 @@ semver@6.3.0, semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@7.3.5, semver@^7.3.5: +semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -8607,15 +8303,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17, source-map-support@^0.5.6: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@~0.5.20: +source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -8842,27 +8530,19 @@ string-width@^4.1.0, string-width@^4.2.0: strip-ansi "^6.0.0" string.prototype.matchall@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" - integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== + version "4.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.19.1" get-intrinsic "^1.1.1" - has-symbols "^1.0.2" + has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" + regexp.prototype.flags "^1.4.1" side-channel "^1.0.4" -string.prototype.trimend@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" - integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -8871,14 +8551,6 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string.prototype.trimstart@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" - integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -8941,14 +8613,7 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-ansi@^6.0.1: +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -8985,12 +8650,7 @@ strip-json-comments@3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -strip-json-comments@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== - -strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -9287,13 +8947,13 @@ ts-node@^8.10.2: yn "3.1.1" tsconfig-paths@^3.12.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz#4fcc48f9ccea8826c41b9ca093479de7f5018976" - integrity sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g== + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" tslib@2.1.0: @@ -9869,18 +9529,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0: +yaml@^1.10.0, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^1.7.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.2.tgz#a29c03f578faafd57dcb27055f9a5d569cb0c3d9" - integrity sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg== - dependencies: - "@babel/runtime" "^7.8.7" - yargs-parser@18.x, yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" From 649ca6ff44e9ccece034a48a22c4aa4301f94e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Tue, 29 Mar 2022 00:36:24 +0200 Subject: [PATCH 08/16] Revert "refactor(oas): rework all translators" This reverts commit 1e330d09c140773bea1f36c38f81e8cf8085a5df. --- package.json | 14 +- src/context.ts | 9 - src/guards.ts | 15 - src/merge.ts | 2 +- .../__snapshots__/operation.test.ts.snap | 83 +----- .../__snapshots__/service.test.ts.snap | 20 ++ src/oas/__tests__/accessors.test.ts | 55 ++-- src/oas/__tests__/service.test.ts | 4 +- src/oas/__tests__/tag.test.ts | 17 ++ src/oas/__tests__/tags.test.ts | 48 ---- src/oas/accessors.ts | 68 ++--- src/oas/guards.ts | 35 +-- src/oas/index.ts | 2 +- src/oas/operation.ts | 45 ++- src/oas/service.ts | 52 ---- src/oas/tag.ts | 5 + src/oas/tags.ts | 45 --- src/oas/transformers/index.ts | 1 + .../schema/__tests__/schema.spec.ts | 5 +- src/oas/transformers/schema/index.ts | 42 ++- .../transformers/schema/keywords/format.ts | 4 +- src/oas/transformers/security.ts | 0 src/oas/transformers/translateLogo.ts | 8 +- src/oas/types.ts | 15 +- .../__snapshots__/operation.test.ts.snap | 11 +- src/oas2/__tests__/accessors.test.ts | 31 +-- src/oas2/__tests__/operation.test.ts | 16 +- src/oas2/__tests__/service.test.ts | 2 + src/oas2/accessors.ts | 50 ++-- src/oas2/guards.ts | 26 +- src/oas2/operation.ts | 64 ++--- src/oas2/service.ts | 121 ++++---- .../__snapshots__/params.test.ts.snap | 8 - .../__snapshots__/request.test.ts.snap | 43 +-- .../__tests__/getExamplesFromSchema.test.ts | 30 ++ .../transformers/__tests__/params.test.ts | 43 +-- .../transformers/__tests__/request.test.ts | 32 +-- .../transformers/__tests__/responses.test.ts | 31 ++- .../transformers/__tests__/securities.test.ts | 7 +- .../transformers/__tests__/servers.test.ts | 9 +- src/oas2/transformers/content.ts | 0 .../transformers/getExamplesFromSchema.ts | 12 + src/oas2/transformers/params.ts | 175 +++++------- src/oas2/transformers/request.ts | 75 ++--- src/oas2/transformers/responses.ts | 65 +++-- src/oas2/transformers/securities.ts | 174 ++++++------ src/oas2/transformers/servers.ts | 62 ++--- src/oas2/types.ts | 10 - .../__snapshots__/operation.test.ts.snap | 11 +- src/oas3/__tests__/operation.test.ts | 47 +++- src/oas3/__tests__/service.test.ts | 8 + src/oas3/accessors.ts | 27 +- src/oas3/guards.ts | 30 +- src/oas3/operation.ts | 35 +-- src/oas3/service.ts | 106 ++++--- .../__snapshots__/content.test.ts.snap | 75 ++--- .../__snapshots__/request.test.ts.snap | 20 +- .../__snapshots__/responses.test.ts.snap | 10 +- .../transformers/__tests__/content.test.ts | 59 +++- .../transformers/__tests__/request.test.ts | 56 ++-- .../transformers/__tests__/responses.test.ts | 9 +- .../transformers/__tests__/securities.test.ts | 8 +- .../transformers/__tests__/servers.test.ts | 107 +------- src/oas3/transformers/callbacks.ts | 54 ++-- src/oas3/transformers/content.ts | 259 +++++++----------- src/oas3/transformers/request.ts | 206 ++++++-------- src/oas3/transformers/responses.ts | 70 +++-- src/oas3/transformers/securities.ts | 41 +-- src/oas3/transformers/servers.ts | 107 ++------ src/oas3/types.ts | 10 - .../transformers/__tests__/params.test.ts | 4 +- src/postman/transformers/params.ts | 8 +- src/postman/transformers/securityScheme.ts | 6 +- src/types.ts | 24 +- src/utils.ts | 21 +- yarn.lock | 64 +---- 76 files changed, 1297 insertions(+), 1876 deletions(-) delete mode 100644 src/context.ts delete mode 100644 src/guards.ts create mode 100644 src/oas/__tests__/tag.test.ts delete mode 100644 src/oas/__tests__/tags.test.ts delete mode 100644 src/oas/service.ts create mode 100644 src/oas/tag.ts delete mode 100644 src/oas/tags.ts delete mode 100644 src/oas/transformers/security.ts create mode 100644 src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts delete mode 100644 src/oas2/transformers/content.ts create mode 100644 src/oas2/transformers/getExamplesFromSchema.ts delete mode 100644 src/oas2/types.ts delete mode 100644 src/oas3/types.ts diff --git a/package.json b/package.json index 6d059d7e..64a29b26 100644 --- a/package.json +++ b/package.json @@ -53,14 +53,13 @@ }, "dependencies": { "@stoplight/json": "^3.17.2", - "@stoplight/types": "^13.0.0-beta.4", - "@types/json-schema": "7.0.5", + "@stoplight/types": "^12.4.0", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", + "@types/urijs": "~1.19.9", + "json-schema": "^0.4.0", "json-schema-generator": "^2.0.6", - "lodash.isequal": "^4.5.0", - "lodash.pick": "^4.4.0", - "lodash.pickby": "^4.6.0", + "lodash": "^4.17.15", "openapi3-ts": "^2.0.1", "postman-collection": "^4.1.0", "tslib": "^2.3.1", @@ -71,9 +70,8 @@ "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", - "@types/lodash.isequal": "^4.5.5", - "@types/lodash.pick": "^4.4.6", - "@types/lodash.pickby": "^4.6.6", + "@types/json-schema": "7.0.5", + "@types/lodash": "4.14.157", "@types/postman-collection": "^3.5.3", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", diff --git a/src/context.ts b/src/context.ts deleted file mode 100644 index d13668e0..00000000 --- a/src/context.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TransformerContext } from './types'; -import { maybeResolveLocalRef } from './utils'; - -export function createContext>(document: T): TransformerContext { - return { - document, - maybeResolveLocalRef: maybeResolveLocalRef.bind(null, document), - }; -} diff --git a/src/guards.ts b/src/guards.ts deleted file mode 100644 index d57fa038..00000000 --- a/src/guards.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function isNonNullable(value: T): value is NonNullable { - return value !== undefined && value !== null; -} - -export function isBoolean(input: unknown): input is boolean { - return typeof input === 'boolean'; -} - -export function isString(value: unknown): value is string { - return typeof value === 'string'; -} - -export function isSerializablePrimitive(value: unknown): value is string | number | boolean | null { - return isBoolean(value) || isString(value) || typeof value === 'number' || value === null; -} diff --git a/src/merge.ts b/src/merge.ts index b007adae..35522069 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -8,7 +8,7 @@ import { IServer, } from '@stoplight/types'; import type { JSONSchema7 as JSONSchema } from 'json-schema'; -import isEqual = require('lodash.isequal'); +import { isEqual } from 'lodash'; function isExclusivelyAnyOfSchema(schema: JSONSchema): schema is { anyOf: JSONSchema[] } { return !!(schema.anyOf && Object.keys(schema).length === 1); diff --git a/src/oas/__tests__/__snapshots__/operation.test.ts.snap b/src/oas/__tests__/__snapshots__/operation.test.ts.snap index be3cd93d..53a1877f 100644 --- a/src/oas/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/operation.test.ts.snap @@ -3,7 +3,6 @@ exports[`oas operation openapi v2 1`] = ` Array [ Object { - "deprecated": false, "description": "", "extensions": Object { "x-another": Object { @@ -13,7 +12,6 @@ Array [ }, "id": "?http-operation-id?", "iid": "addPet", - "internal": false, "method": "post", "path": "/pets", "request": Object { @@ -39,10 +37,6 @@ Array [ "description": "Pet object that needs to be added to the store", "required": true, }, - "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], }, "responses": Array [ Object { @@ -72,11 +66,9 @@ Array [ ], "servers": Array [ Object { - "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -88,19 +80,14 @@ Array [ ], }, Object { - "deprecated": false, "extensions": Object {}, "id": "?http-operation-id?", - "internal": false, "method": "get", "path": "/pets", "request": Object { - "cookie": Array [], "headers": Array [ Object { - "deprecated": false, "name": "Rate-Limit", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -108,12 +95,9 @@ Array [ "style": "simple", }, ], - "path": Array [], "query": Array [ Object { - "deprecated": false, "name": "skip", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -121,9 +105,7 @@ Array [ "style": "form", }, Object { - "deprecated": false, "name": "limit", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -143,11 +125,9 @@ Array [ "security": Array [], "servers": Array [ Object { - "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -155,19 +135,14 @@ Array [ "tags": Array [], }, Object { - "deprecated": false, "extensions": Object {}, "id": "?http-operation-id?", - "internal": false, "method": "options", "path": "/pets", "request": Object { - "cookie": Array [], "headers": Array [ Object { - "deprecated": false, "name": "Rate-Limit", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -175,12 +150,9 @@ Array [ "style": "simple", }, ], - "path": Array [], "query": Array [ Object { - "deprecated": false, "name": "skip", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -188,9 +160,7 @@ Array [ "style": "form", }, Object { - "deprecated": false, "name": "limit", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -206,10 +176,8 @@ Array [ "description": "", "headers": Array [ Object { - "deprecated": false, "description": "Allowed clients", "name": "Allow", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -217,10 +185,8 @@ Array [ "style": "simple", }, Object { - "deprecated": false, "description": "Remaining requests", "name": "X-Rate-Limit", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "int64", @@ -236,11 +202,9 @@ Array [ "security": Array [], "servers": Array [ Object { - "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -248,19 +212,12 @@ Array [ "tags": Array [], }, Object { - "deprecated": false, "description": "", "extensions": Object {}, "id": "?http-operation-id?", - "internal": false, "method": "delete", "path": "/pets", - "request": Object { - "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], - }, + "request": Object {}, "responses": Array [ Object { "code": "400", @@ -278,11 +235,9 @@ Array [ "security": Array [], "servers": Array [ Object { - "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -290,12 +245,10 @@ Array [ "tags": Array [], }, Object { - "deprecated": false, "description": "", "extensions": Object {}, "id": "?http-operation-id?", "iid": "updatePet", - "internal": false, "method": "put", "path": "/pet/{petId}", "request": Object { @@ -321,11 +274,8 @@ Array [ "description": "Pet object that needs to be added to the store", "required": true, }, - "cookie": Array [], - "headers": Array [], "path": Array [ Object { - "deprecated": false, "name": "petId", "required": true, "schema": Object { @@ -335,7 +285,6 @@ Array [ "style": "simple", }, ], - "query": Array [], }, "responses": Array [ Object { @@ -394,11 +343,9 @@ Array [ ], "servers": Array [ Object { - "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -569,12 +516,14 @@ Array [ ], "servers": Array [ Object { - "name": "Swagger Petstore", + "description": undefined, "url": "https://petstore.swagger.io/v2", + "variables": undefined, }, Object { - "name": "Swagger Petstore", + "description": undefined, "url": "http://petstore.swagger.io/v2", + "variables": undefined, }, ], "summary": "Add a new pet to the store", @@ -598,26 +547,20 @@ Array [ "path": Array [], "query": Array [ Object { - "deprecated": false, "examples": Array [], - "explode": false, "name": "skip", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, - "style": "simple", }, Object { - "deprecated": false, "examples": Array [], - "explode": false, "name": "limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, - "style": "simple", }, ], }, @@ -643,12 +586,14 @@ Array [ "security": Array [], "servers": Array [ Object { - "name": "Swagger Petstore", + "description": undefined, "url": "https://petstore.swagger.io/v2", + "variables": undefined, }, Object { - "name": "Swagger Petstore", + "description": undefined, "url": "http://petstore.swagger.io/v2", + "variables": undefined, }, ], "summary": "List pets", @@ -776,15 +721,13 @@ Array [ "headers": Array [], "path": Array [ Object { - "deprecated": false, "examples": Array [], - "explode": false, "name": "petId", + "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, - "style": "simple", }, ], "query": Array [], @@ -849,12 +792,14 @@ Array [ ], "servers": Array [ Object { - "name": "Swagger Petstore", + "description": undefined, "url": "https://petstore.swagger.io/v2", + "variables": undefined, }, Object { - "name": "Swagger Petstore", + "description": undefined, "url": "http://petstore.swagger.io/v2", + "variables": undefined, }, ], "summary": "Update an existing pet", diff --git a/src/oas/__tests__/__snapshots__/service.test.ts.snap b/src/oas/__tests__/__snapshots__/service.test.ts.snap index 62583794..792d26c6 100644 --- a/src/oas/__tests__/__snapshots__/service.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/service.test.ts.snap @@ -37,10 +37,12 @@ Object { ], "servers": Array [ Object { + "description": undefined, "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "description": undefined, "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -48,6 +50,10 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", + "externalDocs": Object { + "description": "Find out more", + "url": "http://swagger.io", + }, "name": "pet", }, Object { @@ -56,6 +62,10 @@ Object { }, Object { "description": "Operations about user", + "externalDocs": Object { + "description": "Find out more about our store", + "url": "http://swagger.io", + }, "name": "user", }, ], @@ -99,10 +109,12 @@ Object { ], "servers": Array [ Object { + "description": undefined, "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "description": undefined, "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -110,6 +122,10 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", + "externalDocs": Object { + "description": "Find out more", + "url": "http://swagger.io", + }, "name": "pet", }, Object { @@ -118,6 +134,10 @@ Object { }, Object { "description": "Operations about user", + "externalDocs": Object { + "description": "Find out more about our store", + "url": "http://swagger.io", + }, "name": "user", }, ], diff --git a/src/oas/__tests__/accessors.test.ts b/src/oas/__tests__/accessors.test.ts index 29879b23..e833c713 100644 --- a/src/oas/__tests__/accessors.test.ts +++ b/src/oas/__tests__/accessors.test.ts @@ -1,29 +1,27 @@ -import { getValidOasParameters } from '../accessors'; -import { OasVersion } from '../types'; +import { getOasTags, getValidOasParameters } from '../accessors'; describe('getOasParameters', () => { it('should return empty array', () => { - expect(getValidOasParameters({}, OasVersion.OAS2, undefined, undefined)).toEqual([]); + expect(getValidOasParameters({}, undefined, undefined)).toEqual([]); }); it('should fallback to operation parameters', () => { expect( getValidOasParameters( {}, - OasVersion.OAS2, [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, + { name: 'n1', in: 'i1' }, + { name: 'n2', in: 'i2' }, ], undefined, ), ).toEqual([ { - in: 'header', + in: 'i1', name: 'n1', }, { - in: 'query', + in: 'i2', name: 'n2', }, ]); @@ -31,17 +29,17 @@ describe('getOasParameters', () => { it('should fallback to path parameters', () => { expect( - getValidOasParameters({}, OasVersion.OAS2, undefined, [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, + getValidOasParameters({}, undefined, [ + { name: 'n1', in: 'i1' }, + { name: 'n2', in: 'i2' }, ]), ).toEqual([ { - in: 'header', + in: 'i1', name: 'n1', }, { - in: 'query', + in: 'i2', name: 'n2', }, ]); @@ -51,30 +49,45 @@ describe('getOasParameters', () => { expect( getValidOasParameters( {}, - OasVersion.OAS3, [ - { name: 'n1', in: 'query', type: 'array' }, - { name: 'no2', in: 'header' }, + { name: 'n1', in: 'n1', type: 'array' }, + { name: 'no2', in: 'io2' }, ], [ - { name: 'n1', in: 'query', type: 'string' }, - { name: 'np3', in: 'header' }, + { name: 'n1', in: 'n1', type: 'string' }, + { name: 'np3', in: 'ip3' }, ], ), ).toEqual([ { - in: 'query', + in: 'n1', name: 'n1', type: 'array', }, { - in: 'header', + in: 'io2', name: 'no2', }, { - in: 'header', + in: 'ip3', name: 'np3', }, ]); }); }); + +describe('getOasTags', () => { + describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { + it('should return empty array', () => { + expect(getOasTags(tags)).toEqual([]); + }); + }); + + it('should filter out invalid values', () => { + expect(getOasTags([{}, null, 'foo'])).toEqual(['foo']); + }); + + it('should normalize values', () => { + expect(getOasTags([0, 'foo', true])).toEqual(['0', 'foo', 'true']); + }); +}); diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 2a0b0884..6a8d8f8a 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -1,7 +1,7 @@ import { transformOas2Service } from '../../oas2/service'; import { transformOas3Service } from '../../oas3/service'; -import * as oas2KitchenSinkJson from './fixtures/oas2-kitchen-sink.json'; -import * as oas3KitchenSinkJson from './fixtures/oas3-kitchen-sink.json'; +import * as oas2KitchenSinkJson from './fixtures//oas2-kitchen-sink.json'; +import * as oas3KitchenSinkJson from './fixtures//oas3-kitchen-sink.json'; describe('oas service', () => { it('openapi v2', () => { diff --git a/src/oas/__tests__/tag.test.ts b/src/oas/__tests__/tag.test.ts new file mode 100644 index 00000000..381e5b43 --- /dev/null +++ b/src/oas/__tests__/tag.test.ts @@ -0,0 +1,17 @@ +import { translateToTags } from '../tag'; + +describe('translateToTags', () => { + it('should translate array of strings to tags', () => { + expect(translateToTags(['a', 'b', 'c'])).toEqual([ + { + name: 'a', + }, + { + name: 'b', + }, + { + name: 'c', + }, + ]); + }); +}); diff --git a/src/oas/__tests__/tags.test.ts b/src/oas/__tests__/tags.test.ts deleted file mode 100644 index 9efcbaa5..00000000 --- a/src/oas/__tests__/tags.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { createContext } from '../../context'; -import { translateToTags as _translateToTags } from '../tags'; - -const translateToTags = (tags: unknown) => _translateToTags.call(createContext({}), tags); - -describe('translateToTags', () => { - describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { - it('should return empty array', () => { - expect(translateToTags(tags)).toStrictEqual([]); - }); - }); - - it('should filter out invalid values', () => { - expect(translateToTags([{}, null, 'foo'])).toStrictEqual([ - { - name: 'foo', - }, - ]); - }); - - it('should normalize values', () => { - expect(translateToTags([0, 'foo', true])).toStrictEqual([ - { - name: '0', - }, - { - name: 'foo', - }, - { - name: 'true', - }, - ]); - }); - - it('should translate array of strings to tags', () => { - expect(translateToTags(['a', 'b', 'c'])).toStrictEqual([ - { - name: 'a', - }, - { - name: 'b', - }, - { - name: 'c', - }, - ]); - }); -}); diff --git a/src/oas/accessors.ts b/src/oas/accessors.ts index 50675aad..5ab865d9 100644 --- a/src/oas/accessors.ts +++ b/src/oas/accessors.ts @@ -1,52 +1,34 @@ -import type { Extensions } from '@stoplight/types'; +import { Extensions } from '@stoplight/types'; +import { fromPairs, isObject, map, unionBy } from 'lodash'; -import { Fragment } from '../types'; -import { entries, maybeResolveLocalRef } from '../utils'; -import { isValidOas2Param, isValidOas3Param, Oas2ParamBase, Oas3ParamBase, ParamBase } from './guards'; -import { OasVersion } from './types'; +import { maybeResolveLocalRef } from '../utils'; -const ROOT_EXTENSIONS = ['x-internal']; +type ParamTypeBase = { name: string; in: string }; -function getParameters(document: Fragment, spec: OasVersion.OAS2, params: unknown): Oas2ParamBase[]; -function getParameters(document: Fragment, spec: OasVersion.OAS3, params: unknown): Oas3ParamBase[]; -function getParameters(document: Fragment, spec: OasVersion, params: unknown): ParamBase[] { - if (!Array.isArray(params)) return []; +const ROOT_EXTENSIONS = ['x-internal']; - const resolved = params.map(maybeResolveLocalRef.bind(null, document)); - return spec === OasVersion.OAS2 ? resolved.filter(isValidOas2Param) : resolved.filter(isValidOas3Param); +export function getValidOasParameters( + document: unknown, + operationParameters: ParamType[] | undefined, + pathParameters: ParamType[] | undefined, +) { + const resolvedOperationParams = map(operationParameters, x => maybeResolveLocalRef(document, x) as ParamType); + const resolvedPathParams = map(pathParameters, x => maybeResolveLocalRef(document, x) as ParamType); + + return unionBy(resolvedOperationParams, resolvedPathParams, (parameter?: ParamType) => { + return isObject(parameter) ? `${parameter.name}-${parameter.in}` : 'invalid'; + }) + .filter(isObject) + .filter(isValidOasParameter); } -const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; - -export function getValidOasParameters( - document: Fragment, - spec: OasVersion.OAS2, - operationParams: unknown, - pathParams: unknown, -): Oas2ParamBase[]; -export function getValidOasParameters( - document: Fragment, - spec: OasVersion.OAS3, - operationParams: unknown, - pathParams: unknown, -): Oas3ParamBase[]; -export function getValidOasParameters( - document: Fragment, - spec: any, - operationParams: unknown, - pathParams: unknown, -): ParamBase[] { - const uniqueParameters: Record = {}; - - const params = [...getParameters(document, spec, operationParams), ...getParameters(document, spec, pathParams)]; - - for (const param of params) { - uniqueParameters[getIdForParameter(param)] ??= param; - } - - return Object.values(uniqueParameters); +const isValidOasParameter = (parameter: Partial): parameter is ParamTypeBase => + 'name' in parameter && typeof parameter.name === 'string' && 'in' in parameter && typeof parameter.in === 'string'; + +export function getOasTags(tags: unknown): string[] { + return Array.isArray(tags) ? tags.filter(tag => typeof tag !== 'object').map(String) : []; } -export function getExtensions(target: unknown): Extensions { - return Object.fromEntries(entries(target).filter(([key]) => key.startsWith('x-') && !ROOT_EXTENSIONS.includes(key))); +export function getExtensions(target: Record): Extensions { + return fromPairs(Object.entries(target).filter(([key]) => key.startsWith('x-') && !ROOT_EXTENSIONS.includes(key))); } diff --git a/src/oas/guards.ts b/src/oas/guards.ts index e0605918..226c5769 100644 --- a/src/oas/guards.ts +++ b/src/oas/guards.ts @@ -1,34 +1,11 @@ -import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial, Dictionary } from '@stoplight/types'; -import { HttpParamStyles } from '@stoplight/types'; -import { ParameterLocation } from 'openapi3-ts'; -import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; -import type { Info } from 'swagger-schema-official'; -import { BaseParameter } from 'swagger-schema-official'; +import { DeepPartial, Dictionary } from '@stoplight/types'; +import { InfoObject } from 'openapi3-ts/src/model/OpenApi'; +import { Info } from 'swagger-schema-official'; + +import { isDictionary } from '../utils'; export function hasXLogo( info: DeepPartial, ): info is DeepPartial & { 'x-logo': Dictionary } { - return isPlainObject(info['x-logo']); + return isDictionary(info['x-logo']); } - -const VALID_OAS3_PARAM_LOCATION: ParameterLocation[] = ['query', 'header', 'path', 'cookie']; -const VALID_OAS2_PARAM_LOCATION: BaseParameter['in'][] = ['query', 'header', 'path', 'body', 'formData']; - -const VALID_PARAM_STYLES: HttpParamStyles[] = Object.values(HttpParamStyles); - -export type Oas3ParamBase = { name: string; in: ParameterLocation }; -export type Oas2ParamBase = { name: string; in: BaseParameter['in'] }; -export type ParamBase = { name: string; in: string }; - -export const isValidOasParam = (param: unknown): param is ParamBase => - isPlainObject(param) && typeof param.name === 'string' && typeof param.in === 'string'; - -export const isValidOas2Param = (param: unknown): param is Oas2ParamBase => - isValidOasParam(param) && VALID_OAS2_PARAM_LOCATION.includes(param.in as BaseParameter['in']); - -export const isValidOas3Param = (param: unknown): param is Oas3ParamBase => - isValidOasParam(param) && VALID_OAS3_PARAM_LOCATION.includes(param.in as ParameterLocation); - -export const isValidParamStyle = (style: unknown): style is HttpParamStyles => - VALID_PARAM_STYLES.includes(style as HttpParamStyles); diff --git a/src/oas/index.ts b/src/oas/index.ts index 633a1ce4..ca4dfd22 100644 --- a/src/oas/index.ts +++ b/src/oas/index.ts @@ -1,2 +1,2 @@ export * from './operation'; -export * from './types'; +export { translateSchemaObject } from './transformers/index'; diff --git a/src/oas/operation.ts b/src/oas/operation.ts index 94bb6864..f4601bf2 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,34 +1,33 @@ -import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial, IHttpOperation } from '@stoplight/types'; -import type { OpenAPIObject } from 'openapi3-ts'; -import type { Spec } from 'swagger-schema-official'; +import { IHttpOperation } from '@stoplight/types'; +import { flatten, get, keys, map } from 'lodash'; +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; -import type { HttpOperationTransformer } from '../types'; +import { HttpOperationTransformer } from '../types'; const DEFAULT_METHODS = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch', 'trace']; export function transformOasOperations( - document: DeepPartial, + document: Spec | OpenAPIObject, transformer: HttpOperationTransformer, methods: string[] | null = DEFAULT_METHODS, ): IHttpOperation[] { - const paths = isPlainObject(document.paths) ? Object.keys(document.paths) : []; + const paths = keys(get(document, 'paths')); - return paths.flatMap(path => { - const value = document.paths![path]; - if (!isPlainObject(value)) return []; + return flatten( + map(paths, path => { + let operations = keys(get(document, ['paths', path])); + if (methods !== null) { + operations = operations.filter(pathKey => methods.includes(pathKey)); + } - let operations = Object.keys(value); - if (methods !== null) { - operations = operations.filter(pathKey => methods.includes(pathKey)); - } - - return operations.map(method => - transformer({ - document, - path, - method, - }), - ); - }); + return operations.map(method => + transformer({ + document, + path, + method, + }), + ); + }), + ); } diff --git a/src/oas/service.ts b/src/oas/service.ts deleted file mode 100644 index c6cb6743..00000000 --- a/src/oas/service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { isPlainObject } from '@stoplight/json'; -import { DeepPartial, IHttpService } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; - -import { isNonNullable, isString } from '../guards'; -import { TranslateFunction } from '../types'; -import { hasXLogo } from './guards'; -import { translateTagDefinition } from './tags'; -import { translateLogo } from './transformers/translateLogo'; - -export const transformOasService: TranslateFunction | DeepPartial, [], IHttpService> = - function () { - const document = this.document; - - const httpService: IHttpService = { - id: '?http-service-id?', - - version: document.info?.version ?? '', - name: document.info?.title ?? 'no-title', - - ...pickBy( - { - description: document.info?.description, - termsOfService: document.info?.termsOfService, - }, - isString, - ), - - ...pickBy( - { - contact: document.info?.contact, - }, - isPlainObject, - ), - }; - - if (isPlainObject(document.info) && hasXLogo(document.info)) { - httpService.logo = translateLogo(document.info); - } - - const tags = Array.isArray(document.tags) - ? document.tags.map(translateTagDefinition, this).filter(isNonNullable) - : []; - - if (tags.length > 0) { - httpService.tags = tags; - } - - return httpService; - }; diff --git a/src/oas/tag.ts b/src/oas/tag.ts new file mode 100644 index 00000000..1d9fd9c2 --- /dev/null +++ b/src/oas/tag.ts @@ -0,0 +1,5 @@ +import { INodeTag } from '@stoplight/types'; + +export function translateToTags(tags: string[]): INodeTag[] { + return tags.map(tag => ({ name: tag })); +} diff --git a/src/oas/tags.ts b/src/oas/tags.ts deleted file mode 100644 index 7f1ec25d..00000000 --- a/src/oas/tags.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { isPlainObject } from '@stoplight/json'; -import type { INodeTag, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); - -import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; -import type { ArrayCallbackParameters, Fragment, TranslateFunction } from '../types'; - -export const translateTag: TranslateFunction< - Fragment, - ArrayCallbackParameters, - Optional -> = function (tag) { - if (tag === null || !isSerializablePrimitive(tag)) return; - - return { - name: String(tag), - }; -}; - -export const translateTagDefinition: TranslateFunction< - Fragment, - ArrayCallbackParameters, - Optional -> = function (tag, ...params) { - if (!isPlainObject(tag)) return; - - const translatedTag = translateTag.call(this, tag.name, ...params); - - if (!translatedTag) return; - - return { - ...translatedTag, - - ...pickBy( - { - description: tag.description, - }, - isString, - ), - }; -}; - -export const translateToTags: TranslateFunction = function (tags) { - return Array.isArray(tags) ? tags.map(translateTag, this).filter(isNonNullable) : []; -}; diff --git a/src/oas/transformers/index.ts b/src/oas/transformers/index.ts index 452614f3..d0152846 100644 --- a/src/oas/transformers/index.ts +++ b/src/oas/transformers/index.ts @@ -1 +1,2 @@ export { translateSchemaObject } from './schema/index'; +export { translateLogo } from './translateLogo'; diff --git a/src/oas/transformers/schema/__tests__/schema.spec.ts b/src/oas/transformers/schema/__tests__/schema.spec.ts index bea202ba..02618bcd 100644 --- a/src/oas/transformers/schema/__tests__/schema.spec.ts +++ b/src/oas/transformers/schema/__tests__/schema.spec.ts @@ -1,8 +1,7 @@ -import { createContext } from '../../../../context'; import { translateSchemaObject } from '..'; -import type { OASSchemaObject } from '../types'; +import { OASSchemaObject } from '../types'; -const translate = (schemaObject: OASSchemaObject) => translateSchemaObject.call(createContext({}), schemaObject); +const translate = (schemaObject: OASSchemaObject) => translateSchemaObject({}, schemaObject); describe('translateSchemaObject', () => { it('should translate id', () => { diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index cd12e29c..225424c2 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -1,12 +1,11 @@ -import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial } from '@stoplight/types'; +import { DeepPartial } from '@stoplight/types'; import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; -import type { OpenAPIObject } from 'openapi3-ts'; -import type { Spec } from 'swagger-schema-official'; +import { isObject } from 'lodash'; +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; -import type { TranslateFunction } from '../../../types'; import keywords from './keywords'; -import type { OASSchemaObject } from './types'; +import { OASSchemaObject } from './types'; const keywordsKeys = Object.keys(keywords); @@ -16,13 +15,10 @@ type InternalOptions = { // Convert from OpenAPI 2.0, OpenAPI 3.0 `SchemaObject` or JSON Schema Draft4/6 to JSON Schema Draft 7 // This converter shouldn't make any differences to Schema objects defined in OpenAPI 3.1, excepts when jsonSchemaDialect is provided. -export const translateSchemaObject: TranslateFunction< - DeepPartial, - [schema: OASSchemaObject], - JSONSchema7 -> = function (schema) { - const document = this.document; - +export function translateSchemaObject( + document: DeepPartial, + schema: OASSchemaObject, +): JSONSchema7 { if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { return { $schema: document.jsonSchemaDialect, @@ -32,11 +28,7 @@ export const translateSchemaObject: TranslateFunction< }; } - return convertSchema(schema); -}; - -export function convertSchema(schema: OASSchemaObject) { - const clonedSchema = _convertSchema(schema, { + const clonedSchema = convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); @@ -44,7 +36,7 @@ export function convertSchema(schema: OASSchemaObject) { return clonedSchema as JSONSchema7; } -function _convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONSchema7 { +function convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONSchema7 { const clonedSchema: OASSchemaObject | JSONSchema7 = { ...schema }; for (const struct of options.structs) { @@ -52,19 +44,19 @@ function _convertSchema(schema: OASSchemaObject, options: InternalOptions): JSON clonedSchema[struct] = clonedSchema[struct].slice(); for (let i = 0; i < clonedSchema[struct].length; i++) { - if (typeof clonedSchema[struct][i] === 'object' && clonedSchema[struct][i] !== null) { - clonedSchema[struct][i] = _convertSchema(clonedSchema[struct][i], options); + if (isObject(clonedSchema[struct][i])) { + clonedSchema[struct][i] = convertSchema(clonedSchema[struct][i], options); } else { clonedSchema[struct].splice(i, 1); i--; } } } else if (clonedSchema[struct] !== null && typeof clonedSchema[struct] === 'object') { - clonedSchema[struct] = _convertSchema(clonedSchema[struct], options); + clonedSchema[struct] = convertSchema(clonedSchema[struct], options); } } - if ('properties' in clonedSchema && isPlainObject(clonedSchema.properties)) { + if ('properties' in clonedSchema && isObject(clonedSchema.properties)) { convertProperties(clonedSchema, options); } @@ -84,8 +76,8 @@ function convertProperties(schema: OASSchemaObject, options: InternalOptions): v for (const key of Object.keys(props)) { const property = props[key]; - if (isPlainObject(property)) { - props[key] = _convertSchema(property, options); + if (isObject(property)) { + props[key] = convertSchema(property, options); } } } diff --git a/src/oas/transformers/schema/keywords/format.ts b/src/oas/transformers/schema/keywords/format.ts index 3ea30f56..9f558e9e 100644 --- a/src/oas/transformers/schema/keywords/format.ts +++ b/src/oas/transformers/schema/keywords/format.ts @@ -1,6 +1,6 @@ -import type { JSONSchema7 } from 'json-schema'; +import { JSONSchema7 } from 'json-schema'; -import type { Converter } from '../types'; +import { Converter } from '../types'; const ranges = { MIN_INT_32: 0 - 2 ** 31, diff --git a/src/oas/transformers/security.ts b/src/oas/transformers/security.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/oas/transformers/translateLogo.ts b/src/oas/transformers/translateLogo.ts index 2e5da953..a7ae8301 100644 --- a/src/oas/transformers/translateLogo.ts +++ b/src/oas/transformers/translateLogo.ts @@ -1,7 +1,7 @@ -import type { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; -import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; -import type { Info } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); +import { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; +import { pickBy } from 'lodash'; +import { InfoObject } from 'openapi3-ts/src/model/OpenApi'; +import { Info } from 'swagger-schema-official'; export function translateLogo({ 'x-logo': logo, diff --git a/src/oas/types.ts b/src/oas/types.ts index 884fa22d..16c7bcb0 100644 --- a/src/oas/types.ts +++ b/src/oas/types.ts @@ -1,8 +1,7 @@ -import { DeepPartial } from '@stoplight/types'; -import type { OpenAPIObject } from 'openapi3-ts'; -import type { Spec } from 'swagger-schema-official'; +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; -import type { +import { HttpOperationTransformer, HttpServiceTransformer, ITransformOperationOpts, @@ -17,15 +16,15 @@ export enum OasVersion { /** * Service */ -export type Oas2TransformServiceOpts = ITransformServiceOpts>; -export type Oas3TransformServiceOpts = ITransformServiceOpts>; +export type Oas2TransformServiceOpts = ITransformServiceOpts; +export type Oas3TransformServiceOpts = ITransformServiceOpts; export type Oas2HttpServiceTransformer = HttpServiceTransformer; export type Oas3HttpServiceTransformer = HttpServiceTransformer; /** * Operation */ -export type Oas2TransformOperationOpts = ITransformOperationOpts>; -export type Oas3TransformOperationOpts = ITransformOperationOpts>; +export type Oas2TransformOperationOpts = ITransformOperationOpts; +export type Oas3TransformOperationOpts = ITransformOperationOpts; export type Oas2HttpOperationTransformer = HttpOperationTransformer; export type Oas3HttpOperationTransformer = HttpOperationTransformer; diff --git a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap index d58bd9bb..9215c3f7 100644 --- a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap @@ -7,15 +7,9 @@ Object { "extensions": Object {}, "id": "?http-operation-id?", "iid": "oid", - "internal": false, "method": "get", "path": "/users/{userId}", - "request": Object { - "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], - }, + "request": Object {}, "responses": Array [ Object { "code": "response", @@ -34,9 +28,8 @@ Object { "description": "desc", "headers": Array [ Object { - "deprecated": false, + "description": undefined, "name": "header", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "integer", diff --git a/src/oas2/__tests__/accessors.test.ts b/src/oas2/__tests__/accessors.test.ts index 08e2b16b..bd8b6965 100644 --- a/src/oas2/__tests__/accessors.test.ts +++ b/src/oas2/__tests__/accessors.test.ts @@ -1,7 +1,7 @@ import { Dictionary } from '@stoplight/types'; import { Security } from 'swagger-schema-official'; -import { getConsumes, getExamplesFromSchema, getProduces, getSecurities } from '../accessors'; +import { getConsumes, getProduces, getSecurities } from '../accessors'; const securityDefinitionsFixture: Dictionary = { api_key: { @@ -268,33 +268,4 @@ describe('accessors', () => { expect(getConsumes({ consumes: ['text/plain', null] } as any, {})).toEqual(['text/plain']); }); }); - - describe('getExamplesFromSchema', () => { - it('should ignore invalid data', () => { - // @ts-ignore - expect(getExamplesFromSchema(null)).toEqual({}); - }); - - it('should work with x-examples', () => { - expect( - getExamplesFromSchema({ - 'x-examples': { - 'my-example': {}, - }, - }), - ).toEqual({ - 'my-example': {}, - }); - }); - - it('should work with example', () => { - expect( - getExamplesFromSchema({ - example: 'my-example', - }), - ).toEqual({ - default: 'my-example', - }); - }); - }); }); diff --git a/src/oas2/__tests__/operation.test.ts b/src/oas2/__tests__/operation.test.ts index 7e010fef..25f64672 100644 --- a/src/oas2/__tests__/operation.test.ts +++ b/src/oas2/__tests__/operation.test.ts @@ -156,16 +156,9 @@ describe('transformOas2Operation', () => { }), { id: '?http-operation-id?', - deprecated: false, - internal: false, path: '/users/{userId}', method: 'put', - request: { - cookie: [], - headers: [], - path: [], - query: [], - }, + request: {}, responses: [], security: [], servers: [], @@ -201,25 +194,18 @@ describe('transformOas2Operation', () => { }), ).toStrictEqual({ id: '?http-operation-id?', - deprecated: false, - internal: false, method: 'get', path: '/users/{userId}', request: { - cookie: [], headers: [ { name: 'name', - required: false, - deprecated: false, schema: { $schema: 'http://json-schema.org/draft-07/schema#', }, style: 'simple', }, ], - path: [], - query: [], }, responses: [], security: [], diff --git a/src/oas2/__tests__/service.test.ts b/src/oas2/__tests__/service.test.ts index 754cd2f5..34e41e9e 100644 --- a/src/oas2/__tests__/service.test.ts +++ b/src/oas2/__tests__/service.test.ts @@ -41,10 +41,12 @@ describe('oas2 service', () => { version: '1.0', servers: [ { + description: void 0, name: '', url: 'https://petstore.swagger.io/v2', }, { + description: void 0, name: '', url: 'http://petstore.swagger.io/v2', }, diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index bbcfe522..e89d0523 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,20 +1,22 @@ -import { isPlainObject } from '@stoplight/json'; import { DeepPartial, Dictionary } from '@stoplight/types'; -import { Operation, Schema, Security, Spec } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); +import { compact, get, isEmpty, isString, keys, map, merge, pickBy } from 'lodash'; +import { negate } from 'lodash/fp'; +import { Operation, Security, Spec } from 'swagger-schema-official'; -import { isNonNullable, isString } from '../guards'; import { isSecurityScheme } from './guards'; export type SecurityWithKey = Security & { key: string }; -export function getSecurities(spec: DeepPartial, operationSecurity: unknown): SecurityWithKey[][] { +export function getSecurities( + spec: DeepPartial, + operationSecurity: Dictionary[] | undefined, +): SecurityWithKey[][] { const globalSecurities = getSecurity(spec.security, spec.securityDefinitions || {}); const operationSecurities = getSecurity(operationSecurity, spec.securityDefinitions || {}); const securities = !!operationSecurity ? operationSecurities : globalSecurities; - return securities.filter(a => a.length); + return securities.filter(negate(isEmpty)); } export function getProduces(spec: DeepPartial, operation: DeepPartial) { @@ -25,21 +27,22 @@ export function getConsumes(spec: DeepPartial, operation: DeepPartial): SecurityWithKey[][] { - if (!Array.isArray(security) || !definitions) { +function getSecurity( + security: DeepPartial, + definitions: DeepPartial, +): SecurityWithKey[][] { + if (!security || !definitions) { return []; } - return security.map(sec => { - if (!isPlainObject(sec)) return []; - return Object.keys(sec) - .map(key => { + return map(security, sec => { + return compact( + keys(sec).map(key => { const def = definitions[key]; if (isSecurityScheme(def)) { - const defCopy = { ...def, key }; - const secKey = sec[key]; - const scopes = Array.isArray(secKey) ? secKey : []; + const defCopy = merge<{ key: string }, Security>({ key }, def); + const scopes = sec[key] || []; // Filter definition scopes by operation scopes if (defCopy.type === 'oauth2' && scopes.length) { @@ -50,8 +53,8 @@ function getSecurity(security: unknown, definitions: DeepPartial, operation: DeepPartial, ): string[] { - const mimeTypes = operation?.[which] || spec?.[which] || []; + const mimeTypes = get(operation, which, get(spec, which, [])); if (!Array.isArray(mimeTypes)) { return []; } - return mimeTypes.flat().filter(isString); -} - -export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Record { - if (!isPlainObject(data)) return {}; - - return { - ...('x-examples' in data && isPlainObject(data['x-examples']) && { ...data['x-examples'] }), - ...('example' in data && { default: data.example }), - }; + return compact(mimeTypes).filter(v => v && isString(v)); } diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index bf5d124f..7708867e 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -1,18 +1,22 @@ -import { isPlainObject } from '@stoplight/json'; +import { isObject } from 'lodash'; import type { Response, Security, Tag } from 'swagger-schema-official'; -import { isValidOas2Param, Oas2ParamBase } from '../oas/guards'; +import { isDictionary } from '../utils'; export function isSecurityScheme(maybeSecurityScheme: unknown): maybeSecurityScheme is Security { - return isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; + return isDictionary(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; } export const isTagObject = (maybeTagObject: unknown): maybeTagObject is Tag => { - return isPlainObject(maybeTagObject) && typeof maybeTagObject.name === 'string'; + if (isObject(maybeTagObject) && 'name' in maybeTagObject) { + return typeof (maybeTagObject as Tag).name === 'string'; + } + + return false; }; export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObject is Response => - isPlainObject(maybeResponseObject) && + isObject(maybeResponseObject) && ('description' in maybeResponseObject || 'schema' in maybeResponseObject || 'headers' in maybeResponseObject || @@ -21,15 +25,3 @@ export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObj export function isValidScheme(scheme: unknown): scheme is 'http' | 'https' | 'ws' | 'wss' { return typeof scheme === 'string' && ['http', 'https', 'ws', 'wss'].includes(scheme); } - -export function isQueryParam(param: unknown): param is Oas2ParamBase & { in: 'query' } { - return isValidOas2Param(param) && param.in === 'query'; -} - -export function isPathParam(param: unknown): param is Oas2ParamBase & { in: 'path' } { - return isValidOas2Param(param) && param.in === 'path'; -} - -export function isHeaderParam(param: unknown): param is Oas2ParamBase & { in: 'header' } { - return isValidOas2Param(param) && param.in === 'header'; -} diff --git a/src/oas2/operation.ts b/src/oas2/operation.ts index 30e022a3..bece36aa 100644 --- a/src/oas2/operation.ts +++ b/src/oas2/operation.ts @@ -1,15 +1,13 @@ -import { isPlainObject } from '@stoplight/json'; -import type { IHttpOperation } from '@stoplight/types'; -import type { Spec } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); +import { IHttpOperation } from '@stoplight/types'; +import { get, isNil, omitBy } from 'lodash'; +import { Operation, Parameter, Path, Response, Spec } from 'swagger-schema-official'; -import { createContext } from '../context'; -import { isString } from '../guards'; -import { getExtensions } from '../oas/accessors'; +import { getExtensions, getOasTags, getValidOasParameters } from '../oas/accessors'; import { transformOasOperations } from '../oas/operation'; -import { translateToTags } from '../oas/tags'; +import { translateToTags } from '../oas/tag'; import { Oas2HttpOperationTransformer } from '../oas/types'; import { maybeResolveLocalRef } from '../utils'; +import { getConsumes, getProduces } from './accessors'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; @@ -20,40 +18,42 @@ export function transformOas2Operations(document: Spec): IHttpOperation[] { } export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]); - if (!isPlainObject(pathObj)) { + const pathObj = maybeResolveLocalRef(document, get(document, ['paths', path])) as Path; + if (!pathObj) { throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); } - const operation = maybeResolveLocalRef(document, pathObj[method]); - if (!isPlainObject(operation)) { + const operation = maybeResolveLocalRef(document, get(document, ['paths', path, method])) as Operation & { + [extension: string]: unknown; + }; + if (!operation) { throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); } - const ctx = createContext(document); + const produces = getProduces(document, operation); + const consumes = getConsumes(document, operation); - return { + const httpOperation: IHttpOperation = { + // TODO(SL-248): what shall we do with id? id: '?http-operation-id?', + iid: operation.operationId, + description: operation.description, + deprecated: operation.deprecated, + internal: operation['x-internal'] as boolean, method, path, - - deprecated: !!operation.deprecated, - internal: !!operation['x-internal'], - - responses: translateToResponses.call(ctx, operation), - servers: translateToServers.call(ctx, operation), - request: translateToRequest.call(ctx, pathObj, operation), - tags: translateToTags.call(ctx, operation.tags), - security: translateToSecurities.call(ctx, operation.security), - extensions: getExtensions(operation), - - ...pickBy( - { - iid: operation.operationId, - description: operation.description, - summary: operation.summary, - }, - isString, + summary: operation.summary, + responses: translateToResponses(document, operation.responses as { [name: string]: Response }, produces), + servers: translateToServers(document, operation), + request: translateToRequest( + document, + getValidOasParameters(document, operation.parameters as Parameter[], pathObj.parameters as Parameter[]), + consumes, ), + tags: translateToTags(getOasTags(operation.tags)), + security: translateToSecurities(document, operation.security), + extensions: getExtensions(operation), }; + + return omitBy(httpOperation, isNil) as IHttpOperation; }; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index a67f48d6..d8eb9f6c 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,17 +1,27 @@ -import { isPlainObject } from '@stoplight/json'; -import pickBy = require('lodash.pickby'); +import { HttpSecurityScheme, IHttpService, IServer } from '@stoplight/types'; +import { compact, filter, flatMap, isString, keys, pickBy } from 'lodash'; -import { createContext } from '../context'; -import { isNonNullable, isString } from '../guards'; -import { transformOasService } from '../oas/service'; +import { hasXLogo } from '../oas/guards'; +import { translateLogo } from '../oas/transformers/translateLogo'; import { Oas2HttpServiceTransformer } from '../oas/types'; -import { entries } from '../utils'; +import { isDictionary } from '../utils'; +import { isTagObject } from './guards'; import { translateToSingleSecurity } from './transformers/securities'; -import { translateToServer } from './transformers/servers'; export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document); - const httpService = transformOasService.call(ctx); + const httpService: IHttpService = { + id: '?http-service-id?', + version: document.info?.version ?? '', + name: document.info?.title ?? 'no-title', + }; + + if (document.info?.description) { + httpService.description = document.info.description; + } + + if (document.info?.contact) { + httpService.contact = document.info.contact; + } if (document.info?.license) { httpService.license = { @@ -20,60 +30,75 @@ export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) = }; } - const schemes = Array.isArray(document.schemes) ? document.schemes.filter(isString) : []; + if (document.info?.termsOfService) { + httpService.termsOfService = document.info.termsOfService; + } - const servers = schemes.map(translateToServer, ctx).filter(isNonNullable); + if (isDictionary(document.info) && hasXLogo(document.info)) { + httpService.logo = translateLogo(document.info); + } + const schemes = filter(document.schemes, scheme => scheme && isString(scheme)); + const servers = schemes.map(scheme => ({ + name: document.info?.title ?? '', + description: undefined, + url: scheme + '://' + (document.host || '') + (document.basePath || ''), + })); if (servers.length) { httpService.servers = servers; } - const securitySchemes = entries(document.securityDefinitions) - .map(([key, definition]) => { - return isPlainObject(definition) ? translateToSingleSecurity.call(ctx, { ...definition, key }) : null; - }) - .filter(isNonNullable); + const securitySchemes = compact( + keys(document.securityDefinitions).map(key => { + const definition = document?.securityDefinitions?.[key]; + if (!definition) return undefined; + return translateToSingleSecurity(definition, key); + }), + ); if (securitySchemes.length) { httpService.securitySchemes = securitySchemes; } - const security = Array.isArray(document.security) - ? document.security - .flatMap(sec => { - if (!isPlainObject(sec)) return null; - - return Object.keys(sec).map(key => { - const ss = securitySchemes.find(securityScheme => securityScheme.key === key); - if (ss && ss.type === 'oauth2') { - const flows = {}; - for (const flowKey in ss.flows) { - const flow = ss.flows[flowKey]; - flows[flowKey] = { - ...flow, - scopes: pickBy(flow.scopes, (_val: string, scopeKey: string) => { - const secKey = sec[key]; - if (secKey) return secKey.includes(scopeKey); - return undefined; - }), - }; - } - - return { - ...ss, - flows, - }; - } - - return ss; - }); - }) - .filter(isNonNullable) - : []; + const security = compact( + flatMap(document.security, sec => { + if (!sec) return null; + + return keys(sec).map(key => { + const ss = securitySchemes.find(securityScheme => securityScheme.key === key); + if (ss && ss.type === 'oauth2') { + const flows = {}; + for (const flowKey in ss.flows) { + const flow = ss.flows[flowKey]; + flows[flowKey] = { + ...flow, + scopes: pickBy(flow.scopes, (_val: string, scopeKey: string) => { + const secKey = sec[key]; + if (secKey) return secKey.includes(scopeKey); + return undefined; + }), + }; + } + + return { + ...ss, + flows, + }; + } + + return ss; + }); + }), + ); if (security.length) { httpService.security = security; } + const tags = filter(document.tags, isTagObject); + if (tags.length) { + httpService.tags = tags; + } + return httpService; }; diff --git a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap index 5c25ea5f..0fe08866 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap @@ -49,7 +49,6 @@ Object { exports[`params.translator translateToHeaderParam should translate header param 1`] = ` Object { - "deprecated": false, "description": "desc", "name": "name", "required": true, @@ -67,10 +66,8 @@ exports[`params.translator translateToHeaderParams should translate empty dictio exports[`params.translator translateToHeaderParams should translate to multiple header params 1`] = ` Array [ Object { - "deprecated": false, "description": "a description", "name": "header-name", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -78,10 +75,8 @@ Array [ "style": "simple", }, Object { - "deprecated": false, "description": "another description", "name": "plain-tex", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -94,10 +89,8 @@ Array [ exports[`params.translator translateToHeaderParams should translate to simple header param 1`] = ` Array [ Object { - "deprecated": false, "description": "a description", "name": "header-name", - "required": false, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", @@ -109,7 +102,6 @@ Array [ exports[`params.translator translateToPathParameter should translate 1`] = ` Object { - "deprecated": false, "description": "descr", "name": "name", "required": true, diff --git a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap index c496d16a..3511832c 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -6,10 +6,6 @@ Object { "in": "body", "name": "param", }, - "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], } `; @@ -20,31 +16,22 @@ Object { "name": "param", "type": "number", }, - "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], } `; exports[`request given single header param should translate to request with header 1`] = ` Object { - "cookie": Array [], "headers": Array [ Object { "in": "header", "name": "param", }, ], - "path": Array [], - "query": Array [], } `; exports[`request given single path param should translate to request with path 1`] = ` Object { - "cookie": Array [], - "headers": Array [], "path": Array [ Object { "in": "path", @@ -52,15 +39,11 @@ Object { "required": true, }, ], - "query": Array [], } `; exports[`request given single query param should translate to request with query 1`] = ` Object { - "cookie": Array [], - "headers": Array [], - "path": Array [], "query": Array [ Object { "in": "query", @@ -72,43 +55,47 @@ Object { exports[`request given two header params should translate 1`] = ` Object { - "cookie": Array [], "headers": Array [ Object { "in": "header", "name": "param", }, + Object { + "in": "header", + "name": "param", + }, ], - "path": Array [], - "query": Array [], } `; exports[`request given two path params should translate 1`] = ` Object { - "cookie": Array [], - "headers": Array [], "path": Array [ Object { "in": "path", "name": "param", "required": true, }, + Object { + "in": "path", + "name": "param", + "required": true, + }, ], - "query": Array [], } `; exports[`request given two query params should translate 1`] = ` Object { - "cookie": Array [], - "headers": Array [], - "path": Array [], "query": Array [ Object { "in": "query", "name": "param", }, + Object { + "in": "query", + "name": "param", + }, ], } `; @@ -120,9 +107,5 @@ Object { "name": "param", "type": "number", }, - "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], } `; diff --git a/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts b/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts new file mode 100644 index 00000000..ceee05df --- /dev/null +++ b/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts @@ -0,0 +1,30 @@ +import { getExamplesFromSchema } from '../getExamplesFromSchema'; + +describe('getExamplesFromSchema', () => { + it('should ignore invalid data', () => { + // @ts-ignore + expect(getExamplesFromSchema(null)).toEqual({}); + }); + + it('should work with x-examples', () => { + expect( + getExamplesFromSchema({ + 'x-examples': { + 'my-example': {}, + }, + }), + ).toEqual({ + 'my-example': {}, + }); + }); + + it('should work with example', () => { + expect( + getExamplesFromSchema({ + example: 'my-example', + }), + ).toEqual({ + default: 'my-example', + }); + }); +}); diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index 2b8158a8..a7e70c25 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -1,42 +1,15 @@ -import { DeepPartial, HttpParamStyles } from '@stoplight/types'; -import { FormDataParameter, QueryParameter, Spec } from 'swagger-schema-official'; +import { HttpParamStyles } from '@stoplight/types'; +import { FormDataParameter, QueryParameter } from 'swagger-schema-official'; -import { createContext } from '../../../context'; import { - translateFromFormDataParameters as _translateFromFormDataParameters, - translateToBodyParameter as _translateToBodyParameter, - translateToHeaderParam as _translateToHeaderParam, - translateToHeaderParams as _translateToHeaderParams, - translateToPathParameter as _translateToPathParameter, - translateToQueryParameter as _translateToQueryParameter, + translateFromFormDataParameters, + translateToBodyParameter, + translateToHeaderParam, + translateToHeaderParams, + translateToPathParameter, + translateToQueryParameter, } from '../params'; -const translateFromFormDataParameters = ( - document: DeepPartial, - ...params: Parameters -) => _translateFromFormDataParameters.call(createContext(document), ...params); - -const translateToBodyParameter = ( - document: DeepPartial, - ...params: Parameters -) => _translateToBodyParameter.call(createContext(document), ...params); - -const translateToHeaderParam = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParam.call(createContext(document), ...params); - -const translateToHeaderParams = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParams.call(createContext(document), ...params); - -const translateToPathParameter = ( - document: DeepPartial, - ...params: Parameters -) => _translateToPathParameter.call(createContext(document), ...params); - -const translateToQueryParameter = ( - document: DeepPartial, - ...params: Parameters -) => _translateToQueryParameter.call(createContext(document), ...params); - describe('params.translator', () => { let consumes = ['*']; diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index 286e6788..da7f43c5 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -6,7 +6,6 @@ import { QueryParameter, } from 'swagger-schema-official'; -import { createContext } from '../../../context'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -14,16 +13,13 @@ import { translateToPathParameter, translateToQueryParameter, } from '../params'; -import { translateToRequest as _translateToRequest } from '../request'; +import { translateToRequest } from '../request'; jest.mock('../params'); - -const translateToRequest = (path: Record, parameters: any[]) => { - const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }); - return _translateToRequest.call(ctx, path, { parameters }); -}; +jest.mock('../../guards'); describe('request', () => { + const consumes = ['*']; const fakeParameter: FormDataParameter = { name: 'name', type: 'string', @@ -47,41 +43,45 @@ describe('request', () => { jest.resetAllMocks(); }); + it('given empty params collection should return empty object', () => { + expect(translateToRequest({}, [], consumes)).toEqual({}); + }); + it('given single body param should translate to request with body', () => { - expect(translateToRequest({}, [fakeBodyParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakeBodyParameter], consumes)).toMatchSnapshot(); }); it('given single form param should translate to request with form', () => { - expect(translateToRequest({}, [fakeFormParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakeFormParameter], consumes)).toMatchSnapshot(); }); it('given single path param should translate to request with path', () => { - expect(translateToRequest({}, [fakePathParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakePathParameter], consumes)).toMatchSnapshot(); }); it('given single query param should translate to request with query', () => { - expect(translateToRequest({}, [fakeQueryParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakeQueryParameter], consumes)).toMatchSnapshot(); }); it('given single header param should translate to request with header', () => { - expect(translateToRequest({}, [fakeHeaderParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakeHeaderParameter], consumes)).toMatchSnapshot(); }); it('given two query params should translate', () => { - expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter], consumes)).toMatchSnapshot(); }); it('given two header params should translate', () => { - expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter], consumes)).toMatchSnapshot(); }); it('given two path params should translate', () => { - expect(translateToRequest({}, [fakePathParameter, fakePathParameter])).toMatchSnapshot(); + expect(translateToRequest({}, [fakePathParameter, fakePathParameter], consumes)).toMatchSnapshot(); }); it('should translate mixed request', () => { expect( - translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter]), + translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter], consumes), ).toMatchSnapshot(); }); }); diff --git a/src/oas2/transformers/__tests__/responses.test.ts b/src/oas2/transformers/__tests__/responses.test.ts index 11881d59..8217087e 100644 --- a/src/oas2/transformers/__tests__/responses.test.ts +++ b/src/oas2/transformers/__tests__/responses.test.ts @@ -1,15 +1,11 @@ -import { DeepPartial, HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; -import { Operation, Schema, Spec } from 'swagger-schema-official'; +import { HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; +import { Schema } from 'swagger-schema-official'; -import { createContext } from '../../../context'; import { translateToHeaderParams } from '../params'; -import { translateToResponses as _translateToResponses } from '../responses'; +import { translateToResponses } from '../responses'; jest.mock('../params'); -const translateToResponses = (document: DeepPartial, responses: DeepPartial) => - _translateToResponses.call(createContext(document), { responses }); - describe('responses', () => { const fakeHeaderParams: IHttpHeaderParam[] = [{ name: 'fake-header', style: HttpParamStyles.Simple }]; const produces = ['application/json', 'application/xml']; @@ -20,7 +16,7 @@ describe('responses', () => { it('should translate to multiple responses', () => { const responses = translateToResponses( - { produces }, + {}, { r1: { description: 'd1', @@ -39,6 +35,7 @@ describe('responses', () => { schema: {}, }, }, + produces, ); expect(responses).toMatchSnapshot(); @@ -47,7 +44,7 @@ describe('responses', () => { it('should translate to response w/o headers', () => { expect( translateToResponses( - { produces }, + {}, { r1: { description: 'd1', @@ -57,6 +54,7 @@ describe('responses', () => { schema: {}, }, }, + produces, ), ).toMatchSnapshot(); }); @@ -64,13 +62,14 @@ describe('responses', () => { it('should translate to response w/o examples', () => { expect( translateToResponses( - { produces }, + {}, { r1: { description: 'd1', schema: {}, }, }, + produces, ), ).toMatchSnapshot(); }); @@ -78,7 +77,7 @@ describe('responses', () => { describe('should keep foreign examples', () => { it('aggregating them to the first example', () => { const responses = translateToResponses( - { produces }, + {}, { r1: { description: 'd1', @@ -90,6 +89,7 @@ describe('responses', () => { schema: {}, }, }, + produces, ); expect(responses[0].contents).toBeDefined(); @@ -104,7 +104,7 @@ describe('responses', () => { describe('given a response with a schema with an example', () => { it('should translate to response with examples', () => { const responses = translateToResponses( - { produces }, + {}, { r1: { description: 'd1', @@ -116,6 +116,7 @@ describe('responses', () => { }, }, }, + produces, ); expect(responses[0].contents![0]).toHaveProperty('examples', [{ key: 'default', value: { name: 'value' } }]); }); @@ -124,7 +125,7 @@ describe('responses', () => { describe('given multiple schema example properties', () => { it('should translate all examples', () => { const responses = translateToResponses( - { produces }, + {}, { r1: { description: 'd1', @@ -141,6 +142,7 @@ describe('responses', () => { } as Schema, }, }, + produces, ); expect(responses[0].contents![0]).toHaveProperty('examples', [ { key: 'application/json', value: { name: 'examples value' } }, @@ -152,7 +154,7 @@ describe('responses', () => { describe('given response with examples in root and schema objects', () => { it('root examples should take precedence over schema examples', () => { const responses = translateToResponses( - { produces }, + {}, { r1: { description: 'd1', @@ -168,6 +170,7 @@ describe('responses', () => { }, }, }, + produces, ); expect(responses[0].contents![0].examples).toHaveLength(2); expect(responses[0].contents![0].examples).toEqual([ diff --git a/src/oas2/transformers/__tests__/securities.test.ts b/src/oas2/transformers/__tests__/securities.test.ts index 9c60c069..9bd8d382 100644 --- a/src/oas2/transformers/__tests__/securities.test.ts +++ b/src/oas2/transformers/__tests__/securities.test.ts @@ -1,11 +1,6 @@ -import { DeepPartial } from '@stoplight/types'; import { Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; -import { translateToSecurities as _translateToSecurities } from '../securities'; - -const translateToSecurities = (document: DeepPartial, ...params: Parameters) => - _translateToSecurities.call(createContext(document), ...params); +import { translateToSecurities } from '../securities'; describe('securities', () => { describe('translateToSecurities', () => { diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 11cd45d6..85d52cee 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,14 +1,7 @@ -import type { DeepPartial } from '@stoplight/types'; -import type { Spec } from 'swagger-schema-official'; - -import { createContext } from '../../../context'; -import { translateToServers as _translateToServers } from '../servers'; +import { translateToServers } from '../servers'; type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; -const translateToServers = (document: DeepPartial, ...params: Parameters) => - _translateToServers.call(createContext(document), ...params); - describe('translateToServers', () => { afterAll(() => { delete (global as GlobalWithLocation).location; diff --git a/src/oas2/transformers/content.ts b/src/oas2/transformers/content.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/oas2/transformers/getExamplesFromSchema.ts b/src/oas2/transformers/getExamplesFromSchema.ts new file mode 100644 index 00000000..b5018f05 --- /dev/null +++ b/src/oas2/transformers/getExamplesFromSchema.ts @@ -0,0 +1,12 @@ +import { Dictionary } from '@stoplight/types'; +import { isObject } from 'lodash'; +import { Schema } from 'swagger-schema-official'; + +export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Dictionary { + if (!isObject(data)) return {}; + + return { + ...('x-examples' in data && isObject(data['x-examples']) && { ...data['x-examples'] }), + ...('example' in data && { default: data.example }), + }; +} diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index f1cb5cda..7527594f 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -1,4 +1,3 @@ -import { isPlainObject } from '@stoplight/json'; import { DeepPartial, HttpParamStyles, @@ -7,31 +6,26 @@ import { IHttpOperationRequestBody, IHttpPathParam, IHttpQueryParam, - Optional, } from '@stoplight/types'; -import type { JSONSchema7 } from 'json-schema'; -import type { +import { JSONSchema7 } from 'json-schema'; +import { get, map, pick, pickBy, set } from 'lodash'; +import { OpenAPIObject } from 'openapi3-ts'; +import { BodyParameter, FormDataParameter, Header, HeaderParameter, PathParameter, QueryParameter, + Spec, } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); -import pick = require('lodash.pick'); -import { isBoolean, isNonNullable, isString } from '../../guards'; -import { Oas2ParamBase } from '../../oas/guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { ArrayCallbackParameters } from '../../types'; -import { entries } from '../../utils'; -import { getExamplesFromSchema } from '../accessors'; -import { isHeaderParam } from '../guards'; -import { Oas2TranslateFunction } from '../types'; +import { isDictionary } from '../../utils'; +import { getExamplesFromSchema } from './getExamplesFromSchema'; function chooseQueryParameterStyle( - parameter: DeepPartial, + parameter: QueryParameter, ): | HttpParamStyles.PipeDelimited | HttpParamStyles.SpaceDelimited @@ -61,40 +55,41 @@ function chooseQueryParameterStyle( } } -export const translateToHeaderParam: Oas2TranslateFunction< - [param: DeepPartial & Oas2ParamBase & { in: 'header' }], - IHttpHeaderParam -> = function (parameter) { - return { - style: HttpParamStyles.Simple, +export function translateToHeaderParam(document: DeepPartial, parameter: HeaderParameter): IHttpHeaderParam { + return pickBy({ + ...buildSchemaForParameter(document, parameter), name: parameter.name, - ...buildSchemaForParameter.call(this, parameter), - required: !!parameter.required, - }; -}; - -const translateToHeaderParamsFromPair: Oas2TranslateFunction< - ArrayCallbackParameters<[name: string, value: unknown]>, - Optional -> = function ([name, value]) { - if (!isPlainObject(value)) return; - const param = { name, in: 'header', ...value }; - if (!isHeaderParam(param)) return; - return translateToHeaderParam.call(this, param); -}; + style: HttpParamStyles.Simple, + required: parameter.required, + }) as unknown as IHttpHeaderParam; +} -export const translateToHeaderParams: Oas2TranslateFunction<[headers: unknown], IHttpHeaderParam[]> = function ( - headers, -) { - return entries(headers).map(translateToHeaderParamsFromPair, this).filter(isNonNullable); -}; +export function translateToHeaderParams( + document: DeepPartial, + headers: { [headerName: string]: Header }, +): IHttpHeaderParam[] { + return map(headers, (header, name) => { + const { schema, description } = buildSchemaForParameter(document, Object.assign({ name }, header)); + + const param: IHttpHeaderParam = { + name, + style: HttpParamStyles.Simple, + schema, + description, + }; + + return param; + }); +} -export const translateToBodyParameter: Oas2TranslateFunction< - [body: BodyParameter, consumes: string[]], - IHttpOperationRequestBody -> = function (body, consumes) { - const examples = entries(body['x-examples'] || (body.schema ? getExamplesFromSchema(body.schema) : void 0)).map( - ([key, value]) => ({ key, value }), +export function translateToBodyParameter( + document: DeepPartial, + body: BodyParameter, + consumes: string[], +): IHttpOperationRequestBody { + const examples = map( + get(body, 'x-examples') || (body.schema ? getExamplesFromSchema(body.schema) : void 0), + (value, key) => ({ key, value }), ); return pickBy({ @@ -103,17 +98,18 @@ export const translateToBodyParameter: Oas2TranslateFunction< contents: consumes.map(mediaType => { return { mediaType, - schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, + schema: isDictionary(body.schema) ? translateSchemaObject(document, body.schema) : void 0, examples, }; }), }); -}; +} -export const translateFromFormDataParameters: Oas2TranslateFunction< - [parameters: FormDataParameter[], consumes: string[]], - IHttpOperationRequestBody -> = function (parameters, consumes) { +export function translateFromFormDataParameters( + document: DeepPartial, + parameters: FormDataParameter[], + consumes: string[], +): IHttpOperationRequestBody { const finalBody: IHttpOperationRequestBody = { contents: consumes.map(mediaType => ({ mediaType, @@ -125,7 +121,7 @@ export const translateFromFormDataParameters: Oas2TranslateFunction< }; return parameters.reduce((body, parameter) => { - const { schema, description } = buildSchemaForParameter.call(this, parameter); + const { schema, description } = buildSchemaForParameter(document, parameter); (body.contents || []).forEach(content => { delete schema.$schema; @@ -133,12 +129,11 @@ export const translateFromFormDataParameters: Oas2TranslateFunction< schema.description = description; } - content.schema ||= {}; - content.schema.properties ||= {}; - content.schema.properties[parameter.name] = schema; + set(content, `schema.properties.${parameter.name}`, schema); if (parameter.required) { - (content.schema.required ||= []).push(parameter.name); + const requiredIndex = get(content, 'schema.required.length', 0); + set(content, `schema.required.${requiredIndex}`, parameter.name); } if (parameter.collectionFormat) { @@ -151,7 +146,7 @@ export const translateFromFormDataParameters: Oas2TranslateFunction< }); return body; }, finalBody); -}; +} function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { switch (parameter.collectionFormat) { @@ -183,41 +178,29 @@ function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { return null; } -export const translateToQueryParameter: Oas2TranslateFunction< - [query: DeepPartial & Oas2ParamBase], - IHttpQueryParam -> = function (query) { - return { - style: chooseQueryParameterStyle(query), +export function translateToQueryParameter(document: DeepPartial, query: QueryParameter): IHttpQueryParam { + return pickBy({ + ...buildSchemaForParameter(document, query), + allowEmptyValue: query.allowEmptyValue, name: query.name, - required: !!query.required, - ...buildSchemaForParameter.call(this, query), - - ...pickBy( - { - allowEmptyValue: query.allowEmptyValue, - }, - isBoolean, - ), - }; -}; + style: chooseQueryParameterStyle(query), + required: query.required, + }) as unknown as IHttpQueryParam; +} -export const translateToPathParameter: Oas2TranslateFunction< - [param: DeepPartial & Oas2ParamBase], - IHttpPathParam -> = function (param) { - return { - name: param.name, +export function translateToPathParameter(document: DeepPartial, parameter: PathParameter): IHttpPathParam { + return pickBy({ + ...buildSchemaForParameter(document, parameter), + name: parameter.name, style: HttpParamStyles.Simple, - required: !!param.required, - ...buildSchemaForParameter.call(this, param), - }; -}; + required: parameter.required, + }) as unknown as IHttpPathParam; +} -const buildSchemaForParameter: Oas2TranslateFunction< - [param: DeepPartial], - { schema: JSONSchema7; description?: string; deprecated?: boolean } -> = function (param) { +function buildSchemaForParameter( + document: DeepPartial, + param: QueryParameter | PathParameter | HeaderParameter | FormDataParameter | Header, +): { schema: JSONSchema7; description?: string } { const schema = pick( param, 'type', @@ -237,21 +220,15 @@ const buildSchemaForParameter: Oas2TranslateFunction< 'pattern', 'uniqueItems', 'multipleOf', - ) as Record; + ); if ('allowEmptyValue' in param && param.allowEmptyValue === false) { schema.minLength = 1; } return { - schema: translateSchemaObject.call(this, schema), - deprecated: !!param['x-deprecated'], - - ...pickBy( - { - description: param.description, - }, - isString, - ), + schema: translateSchemaObject(document, schema), + description: param.description, + ...('x-deprecated' in param && { deprecated: param['x-deprecated'] }), }; -}; +} diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index cdf894e3..e7763ce3 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,13 +1,6 @@ -import type { IHttpOperationRequest } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { BodyParameter, FormDataParameter } from 'swagger-schema-official'; +import { DeepPartial, IHttpOperationRequest } from '@stoplight/types'; +import { BodyParameter, FormDataParameter, Parameter, Spec } from 'swagger-schema-official'; -import { isNonNullable } from '../../guards'; -import { OasVersion } from '../../oas'; -import { getValidOasParameters } from '../../oas/accessors'; -import { getConsumes } from '../accessors'; -import { isHeaderParam, isPathParam, isQueryParam } from '../guards'; -import { Oas2TranslateFunction } from '../types'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -16,50 +9,38 @@ import { translateToQueryParameter, } from './params'; -export const translateToRequest: Oas2TranslateFunction< - [path: Record, operation: Record], - IHttpOperationRequest -> = function (path, operation) { - const consumes = getConsumes(this.document, operation); - const parameters = getValidOasParameters(this.document, OasVersion.OAS2, operation.parameters, path.parameters); - - const bodyParameter = parameters.find((p): p is BodyParameter => p.in === 'body'); +export function translateToRequest( + document: DeepPartial, + parameters: Parameter[], + consumes: string[], +): IHttpOperationRequest { + const bodyParameters = parameters.filter((p): p is BodyParameter => p.in === 'body'); const formDataParameters = parameters.filter((p): p is FormDataParameter => p.in === 'formData'); + const request: IHttpOperationRequest = {}; - const params: Omit, 'body'> = { - headers: [], - query: [], - cookie: [], - path: [], - }; - - let body; // if 'body' and 'form data' defined prefer 'body' - if (!!bodyParameter) { + if (!!bodyParameters.length) { // There can be only one body parameter (taking first one) - body = translateToBodyParameter.call(this, bodyParameter, consumes); + request.body = translateToBodyParameter(document, bodyParameters[0], consumes); } else if (!!formDataParameters.length) { - body = translateFromFormDataParameters.call(this, formDataParameters, consumes); + request.body = translateFromFormDataParameters(document, formDataParameters, consumes); } - for (const param of parameters) { - if (isQueryParam(param)) { - params.query.push(translateToQueryParameter.call(this, param)); - } else if (isPathParam(param)) { - params.path.push(translateToPathParameter.call(this, param)); - } else if (isHeaderParam(param)) { - params.headers.push(translateToHeaderParam.call(this, param)); + return parameters.reduce(createReduceRemainingParameters(document), request); +} + +function createReduceRemainingParameters(document: DeepPartial) { + return function (request: IHttpOperationRequest, parameter: Parameter) { + if (parameter.in === 'query') { + const queryParameter = translateToQueryParameter(document, parameter); + request.query = (request.query || []).concat(queryParameter); + } else if (parameter.in === 'path') { + const pathParameter = translateToPathParameter(document, parameter); + request.path = (request.path || []).concat(pathParameter); + } else if (parameter.in === 'header') { + const headerParameter = translateToHeaderParam(document, parameter); + request.headers = (request.headers || []).concat(headerParameter); } - } - - return { - ...params, - - ...pickBy( - { - body, - }, - isNonNullable, - ), + return request; }; -}; +} diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index 34b58bd8..78592e70 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,34 +1,34 @@ -import { isPlainObject } from '@stoplight/json'; -import type { IHttpOperationResponse, Optional } from '@stoplight/types'; -import { DeepPartial } from '@stoplight/types'; -import { Operation } from 'swagger-schema-official'; +import type { DeepPartial, Dictionary, IHttpOperationResponse, Optional } from '@stoplight/types'; +import { chain, compact, map, partial } from 'lodash'; +import type { Spec } from 'swagger-schema-official'; -import { isNonNullable } from '../../guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { entries } from '../../utils'; -import { getExamplesFromSchema, getProduces } from '../accessors'; +import { isDictionary, maybeResolveLocalRef } from '../../utils'; import { isResponseObject } from '../guards'; -import { Oas2TranslateFunction } from '../types'; +import { getExamplesFromSchema } from './getExamplesFromSchema'; import { translateToHeaderParams } from './params'; -const translateToResponse: Oas2TranslateFunction< - [produces: string[], statusCode: string, response: unknown], - Optional -> = function (produces, statusCode, response) { - const resolvedResponse = this.maybeResolveLocalRef(response); +function translateToResponse( + document: DeepPartial, + produces: string[], + response: unknown, + statusCode: string, +): Optional { + const resolvedResponse = maybeResolveLocalRef(document, response); if (!isResponseObject(resolvedResponse)) return; - const headers = translateToHeaderParams.call(this, resolvedResponse.headers); - const objectifiedExamples = entries( + const headers = translateToHeaderParams(document, resolvedResponse.headers || {}); + const objectifiedExamples = chain( resolvedResponse.examples || (resolvedResponse.schema ? getExamplesFromSchema(resolvedResponse.schema) : void 0), - ).map(([key, value]) => ({ key, value })); + ) + .mapValues((value, key) => ({ key, value })) + .values() + .value(); const contents = produces .map(produceElement => ({ mediaType: produceElement, - schema: isPlainObject(resolvedResponse.schema) - ? translateSchemaObject.call(this, resolvedResponse.schema) - : void 0, + schema: isDictionary(resolvedResponse.schema) ? translateSchemaObject(document, resolvedResponse.schema) : void 0, examples: objectifiedExamples.filter(example => example.key === produceElement), })) .filter(({ schema, examples }) => !!schema || examples.length > 0); @@ -53,14 +53,21 @@ const translateToResponse: Oas2TranslateFunction< } return translatedResponses; -}; +} -export const translateToResponses: Oas2TranslateFunction< - [operation: DeepPartial], - IHttpOperationResponse[] -> = function (operation) { - const produces = getProduces(this.document, operation); - return entries(operation.responses) - .map(([statusCode, response]) => translateToResponse.call(this, produces, statusCode, response)) - .filter(isNonNullable); -}; +export function translateToResponses( + document: DeepPartial, + responses: unknown, + produces: string[], +): IHttpOperationResponse[] { + if (!isDictionary(responses)) { + return []; + } + + return compact( + map, Optional>( + responses, + partial(translateToResponse, document, produces), + ), + ); +} diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index c7123286..aaf49c37 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -1,135 +1,141 @@ -import { isPlainObject } from '@stoplight/json'; -import type { +import { DeepPartial, + Dictionary, + HttpSecurityScheme, IApiKeySecurityScheme, IBasicSecurityScheme, IOauth2SecurityScheme, IOauthFlowObjects, - Optional, } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { +import { isString, pickBy } from 'lodash'; +import { ApiKeySecurity, - BasicAuthenticationSecurity, OAuth2AccessCodeSecurity, OAuth2ApplicationSecurity, OAuth2ImplicitSecurity, OAuth2PasswordSecurity, + Security, + Spec, } from 'swagger-schema-official'; -import { isNonNullable, isString } from '../../guards'; import { SecurityWithKey } from '../../oas3/accessors'; +import { isDictionary } from '../../utils'; import { getSecurities } from '../accessors'; import { isSecurityScheme } from '../guards'; -import { Oas2TranslateFunction } from '../types'; - -export const translateToFlows: Oas2TranslateFunction<[security: Record], IOauthFlowObjects> = - function (security) { - const flows: IOauthFlowObjects = {}; - - const scopes = isPlainObject(security.scopes) ? pickBy(security.scopes, isString) : {}; - const authorizationUrl = - 'authorizationUrl' in security && typeof security.authorizationUrl === 'string' ? security.authorizationUrl : ''; - const tokenUrl = 'tokenUrl' in security && typeof security.tokenUrl === 'string' ? security.tokenUrl : ''; - - if (security.flow === 'implicit') { - flows.implicit = { - authorizationUrl, - scopes, - }; - } else if (security.flow === 'password') { - flows.password = { - tokenUrl, - scopes, - }; - } else if (security.flow === 'application') { - flows.clientCredentials = { - tokenUrl, - scopes, - }; - } else if (security.flow === 'accessCode') { - flows.authorizationCode = { - authorizationUrl, - tokenUrl, - scopes, - }; - } - return flows; - }; +/** + * @param security the union with 'any' is purposeful. Passing strict types does not help much here, + * because all these checks happen in runtime. I'm leaving 'Security' only for visibility. + */ +function translateToFlows(security: Dictionary): IOauthFlowObjects { + const flows: IOauthFlowObjects = {}; + + const scopes = isDictionary(security.scopes) ? pickBy(security.scopes, isString) : {}; + const authorizationUrl = + 'authorizationUrl' in security && typeof security.authorizationUrl === 'string' ? security.authorizationUrl : ''; + const tokenUrl = 'tokenUrl' in security && typeof security.tokenUrl === 'string' ? security.tokenUrl : ''; + + if (security.flow === 'implicit') { + flows.implicit = { + authorizationUrl, + scopes, + }; + } else if (security.flow === 'password') { + flows.password = { + tokenUrl, + scopes, + }; + } else if (security.flow === 'application') { + flows.clientCredentials = { + tokenUrl, + scopes, + }; + } else if (security.flow === 'accessCode') { + flows.authorizationCode = { + authorizationUrl, + tokenUrl, + scopes, + }; + } + + return flows; +} -export const translateToBasicSecurityScheme: Oas2TranslateFunction< - [security: DeepPartial & { key: string }], - IBasicSecurityScheme -> = function (security) { +function translateToBasicSecurityScheme(security: DeepPartial, key: string): IBasicSecurityScheme { return { type: 'http', scheme: 'basic', description: security.description, - key: security.key, + key, }; -}; +} -const ACCEPTABLE_SECURITY_ORIGINS: ApiKeySecurity['in'][] = ['query', 'header']; +function translateToApiKeySecurityScheme( + security: DeepPartial, + key: string, +): IApiKeySecurityScheme | undefined { + const acceptableSecurityOrigins: ApiKeySecurity['in'][] = ['query', 'header']; -export const translateToApiKeySecurityScheme: Oas2TranslateFunction< - [security: DeepPartial & { key: string }], - Optional -> = function (security) { - if ('in' in security && security.in && ACCEPTABLE_SECURITY_ORIGINS.includes(security.in)) { + if ('in' in security && security.in && acceptableSecurityOrigins.includes(security.in)) { return { type: 'apiKey', + name: security.name || '', in: security.in, - name: String(security.name || ''), description: security.description, - key: security.key, + key, }; } - return; -}; + return undefined; +} const VALID_OAUTH2_FLOWS = ['implicit', 'password', 'application', 'accessCode']; -export const translateToOauth2SecurityScheme: Oas2TranslateFunction< - [ - security: DeepPartial< - OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity - > & { key: string }, - ], - Optional -> = function (security) { +function translateToOauth2SecurityScheme( + security: DeepPartial< + OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity + >, + key: string, +): IOauth2SecurityScheme | undefined { if (!security.flow || !VALID_OAUTH2_FLOWS.includes(security.flow)) return undefined; return { type: 'oauth2', - flows: translateToFlows.call(this, security), + flows: translateToFlows(security), description: security.description, - key: security.key, + key, }; -}; +} -export const translateToSingleSecurity: Oas2TranslateFunction< - [security: unknown & { key: string }], - Optional -> = function (security) { +export function translateToSingleSecurity(security: unknown, key: string) { if (isSecurityScheme(security)) { switch (security.type) { case 'basic': - return translateToBasicSecurityScheme.call(this, security); + return translateToBasicSecurityScheme(security, key); case 'apiKey': - return translateToApiKeySecurityScheme.call(this, security); + return translateToApiKeySecurityScheme(security, key); case 'oauth2': - return translateToOauth2SecurityScheme.call(this, security); + return translateToOauth2SecurityScheme(security, key); } } return; -}; - -export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = - function (operationSecurities) { - const securities = getSecurities(this.document, operationSecurities); - - return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); - }; +} + +export function translateToSecurities( + document: DeepPartial, + operationSecurity: Dictionary[] | undefined, +): HttpSecurityScheme[][] { + const securities = getSecurities(document, operationSecurity); + + return securities.map(security => + security.reduce((transformedSecurities, sec) => { + const transformed = translateToSingleSecurity(sec, sec.key); + if (transformed) { + transformedSecurities.push(transformed); + } + + return transformedSecurities; + }, []), + ); +} diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index 7be41c5d..2dee9fd6 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,52 +1,32 @@ -import type { IServer, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import { DeepPartial, IServer } from '@stoplight/types'; +import { Operation, Spec } from 'swagger-schema-official'; -import { isNonNullable, isString } from '../../guards'; import { isValidScheme } from '../guards'; -import type { Oas2TranslateFunction } from '../types'; -export const translateToServers: Oas2TranslateFunction<[operation: Record], IServer[]> = function ( - operation, -) { - let schemes; - if (Array.isArray(operation.schemes)) { - schemes = operation.schemes; - } else if (Array.isArray(this.document.schemes)) { - schemes = this.document.schemes; - } else { +export function translateToServers(spec: DeepPartial, operation: DeepPartial): IServer[] { + const { host } = spec; + if (typeof host !== 'string' || host.length === 0) { return []; } - return schemes.map(translateToServer, this).filter(isNonNullable); -}; - -export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optional> = function (scheme) { - const { host } = this.document; - if (typeof host !== 'string' || host.length === 0) { - return; + const schemes = operation.schemes || spec.schemes; + if (!Array.isArray(schemes)) { + return []; } - if (!isString(scheme) || !isValidScheme(scheme)) return; + const basePath = typeof spec.basePath === 'string' && spec.basePath.length > 0 ? spec.basePath : null; - const basePath = - typeof this.document.basePath === 'string' && this.document.basePath.length > 0 ? this.document.basePath : null; - - const uri = new URL('https://localhost'); - uri.host = host; - uri.protocol = `${scheme}:`; - - if (basePath !== null) { - uri.pathname = basePath; - } + return schemes.filter(isValidScheme).map(scheme => { + const uri = new URL('https://localhost'); + uri.host = host; + uri.protocol = `${scheme}:`; - return { - url: uri.toString().replace(/\/$/, ''), // Remove trailing slash + if (basePath !== null) { + uri.pathname = basePath; + } - ...pickBy( - { - name: this.document.info?.title, - }, - isString, - ), - }; -}; + return { + url: uri.toString().replace(/\/$/, ''), // Remove trailing slash + }; + }); +} diff --git a/src/oas2/types.ts b/src/oas2/types.ts deleted file mode 100644 index dcfe7a17..00000000 --- a/src/oas2/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { DeepPartial } from '@stoplight/types'; -import type { Spec } from 'swagger-schema-official'; - -import { TranslateFunction } from '../types'; - -export type Oas2TranslateFunction

= TranslateFunction< - DeepPartial, - P, - R ->; diff --git a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap index 92aa3bb3..b9e22450 100644 --- a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap @@ -48,6 +48,8 @@ Object { }, }, ], + "description": undefined, + "required": undefined, }, "cookie": Array [], "headers": Array [], @@ -109,8 +111,9 @@ Object { "security": Array [], "servers": Array [ Object { - "name": "title", + "description": undefined, "url": "operation/server", + "variables": undefined, }, ], "summary": "summary", @@ -140,8 +143,9 @@ Object { "security": Array [], "servers": Array [ Object { - "name": "title", + "description": undefined, "url": "path/server", + "variables": undefined, }, ], "summary": "summary", @@ -201,8 +205,9 @@ Object { "security": Array [], "servers": Array [ Object { - "name": "title", + "description": undefined, "url": "spec/server", + "variables": undefined, }, ], "summary": "summary", diff --git a/src/oas3/__tests__/operation.test.ts b/src/oas3/__tests__/operation.test.ts index fdba8752..7152e034 100644 --- a/src/oas3/__tests__/operation.test.ts +++ b/src/oas3/__tests__/operation.test.ts @@ -339,7 +339,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - name: 'title', + description: void 0, url: 'operation/server', }, ]); @@ -437,7 +437,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - name: 'title', + description: void 0, url: 'path/server', }, ]); @@ -535,7 +535,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - name: 'title', + description: void 0, url: 'spec/server', }, ]); @@ -642,9 +642,6 @@ describe('transformOas3Operation', () => { }, ], name: 'name', - deprecated: false, - explode: false, - style: 'simple', schema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', @@ -721,9 +718,6 @@ describe('transformOas3Operation', () => { value: 'some example', }, ], - deprecated: false, - explode: false, - style: 'simple', name: 'name', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -793,14 +787,18 @@ describe('transformOas3Operation', () => { variables: { basePath: { default: 'v2', + description: void 0, + enum: void 0, }, port: { default: '8443', + description: void 0, enum: ['8443', '443'], }, username: { default: 'demo', description: 'value is assigned by the service provider', + enum: void 0, }, }, }, @@ -901,7 +899,9 @@ describe('transformOas3Operation', () => { encodings: [], examples: [ { + description: undefined, key: 'pet-shared', + summary: undefined, value: { type: 'object', properties: { @@ -913,10 +913,12 @@ describe('transformOas3Operation', () => { }, }, { + description: undefined, key: 'pet-not-shared', value: { 'not-shared': true, }, + summary: undefined, }, ], mediaType: 'application/json', @@ -927,6 +929,8 @@ describe('transformOas3Operation', () => { }, }, ], + description: undefined, + required: undefined, }, cookie: [], headers: [], @@ -941,7 +945,9 @@ describe('transformOas3Operation', () => { encodings: [], examples: [ { + description: undefined, key: 'pet-shared', + summary: undefined, value: { properties: { id: { @@ -953,7 +959,9 @@ describe('transformOas3Operation', () => { }, }, { + description: undefined, key: 'pet-not-shared', + summary: undefined, value: { 'not-shared': true, }, @@ -967,6 +975,7 @@ describe('transformOas3Operation', () => { }, }, ], + description: undefined, headers: [], }, ], @@ -1242,25 +1251,30 @@ describe('transformOas3Operation', () => { request: { body: { contents: [ - expect.objectContaining({ + { + encodings: [], + examples: [], + mediaType: 'application/json', schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }), + }, ], }, cookie: [], headers: [ - expect.objectContaining({ + { + examples: [{ key: 'default', value: 'test' }], + name: 'email', schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'string', format: 'email', example: 'test', }, - }), + }, ], path: [], query: [], @@ -1269,13 +1283,16 @@ describe('transformOas3Operation', () => { { code: '200', contents: [ - expect.objectContaining({ + { + encodings: [], + examples: [], + mediaType: 'application/json', schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }), + }, ], headers: [], }, diff --git a/src/oas3/__tests__/service.test.ts b/src/oas3/__tests__/service.test.ts index 523ac541..01ea45b9 100644 --- a/src/oas3/__tests__/service.test.ts +++ b/src/oas3/__tests__/service.test.ts @@ -115,10 +115,12 @@ describe('oas3 service', () => { version: '1.0', servers: [ { + description: void 0, name: '', url: 'https://petstore.swagger.io/v2', }, { + description: void 0, name: '', url: 'http://petstore.swagger.io/v2', }, @@ -178,23 +180,29 @@ describe('oas3 service', () => { servers: [ { description: 'Sample Petstore Server Https', + name: '', url: 'https://petstore.swagger.io/v2', variables: { basePath: { default: 'v2', + description: void 0, + enum: void 0, }, port: { default: '8443', + description: void 0, enum: ['8443', '443'], }, username: { default: 'demo', description: 'value is assigned by the service provider', + enum: void 0, }, }, }, { description: 'Sample Petstore Server Http', + name: '', url: 'http://petstore.swagger.io/v2', }, ], diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index 38f66cf5..c637efff 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,9 +1,7 @@ -import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, Dictionary, HttpSecurityScheme } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { OpenAPIObject } from 'openapi3-ts'; +import { isObject, mapValues, pickBy } from 'lodash'; +import { OAuthFlowObject, OpenAPIObject } from 'openapi3-ts'; -import { entries } from '../utils'; import { isSecurityScheme, isSecuritySchemeWithKey } from './guards'; export type OperationSecurities = Dictionary[] | undefined; @@ -11,14 +9,14 @@ export type SecurityWithKey = HttpSecurityScheme & { key: string }; export function getSecurities( document: DeepPartial, - operationSecurities?: unknown, + operationSecurities?: OperationSecurities, ): SecurityWithKey[][] { const definitions = document.components?.securitySchemes; - if (!isPlainObject(definitions)) return []; + if (!isObject(definitions)) return []; - return (Array.isArray(operationSecurities) ? operationSecurities : document.security || []).map(operationSecurity => { - return entries(operationSecurity) + return (operationSecurities || document.security || []).map(operationSecurity => { + return Object.entries(operationSecurity) .map(([opScheme, scopes]) => { const definition = definitions[opScheme]; @@ -26,15 +24,10 @@ export function getSecurities( // Put back only the flows that are part of the current definition return { ...definition, - flows: Object.fromEntries( - entries(definition.flows).map(([name, flow]) => [ - name, - { - ...flow, - scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), - }, - ]), - ), + flows: mapValues(definition.flows, (flow: OAuthFlowObject) => ({ + ...flow, + scopes: pickBy(flow.scopes, (_val: string, key: string) => scopes?.includes(key)), + })), key: opScheme, }; } diff --git a/src/oas3/guards.ts b/src/oas3/guards.ts index 3bac2280..c2cdf920 100644 --- a/src/oas3/guards.ts +++ b/src/oas3/guards.ts @@ -1,5 +1,6 @@ -import { isPlainObject } from '@stoplight/json'; -import type { +import { Dictionary } from '@stoplight/types'; +import { isObject } from 'lodash'; +import { BaseParameterObject, HeaderObject, OAuthFlowObject, @@ -11,18 +12,19 @@ import type { TagObject, } from 'openapi3-ts'; -import type { SecurityWithKey } from './accessors'; +import { isDictionary } from '../utils'; +import { SecurityWithKey } from './accessors'; export const isSecurityScheme = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecuritySchemeObject => - isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; + isObject(maybeSecurityScheme) && typeof (maybeSecurityScheme as Dictionary).type === 'string'; export const isSecuritySchemeWithKey = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecurityWithKey => - isSecurityScheme(maybeSecurityScheme) && typeof maybeSecurityScheme.key === 'string'; + isSecurityScheme(maybeSecurityScheme) && typeof (maybeSecurityScheme as Dictionary).key === 'string'; export const isBaseParameterObject = ( maybeBaseParameterObject: unknown, ): maybeBaseParameterObject is BaseParameterObject => - isPlainObject(maybeBaseParameterObject) && + isObject(maybeBaseParameterObject) && ('description' in maybeBaseParameterObject || 'required' in maybeBaseParameterObject || 'content' in maybeBaseParameterObject || @@ -36,33 +38,33 @@ export const isHeaderObject = (maybeHeaderObject: unknown): maybeHeaderObject is isBaseParameterObject(maybeHeaderObject); export const isServerObject = (maybeServerObject: unknown): maybeServerObject is ServerObject => - isPlainObject(maybeServerObject) && typeof maybeServerObject.url === 'string'; + isObject(maybeServerObject) && typeof (maybeServerObject as Dictionary).url === 'string'; export const isServerVariableObject = ( maybeServerVariableObject: unknown, ): maybeServerVariableObject is ServerVariableObject => { - if (!isPlainObject(maybeServerVariableObject)) return false; - const typeofDefault = typeof maybeServerVariableObject.default; + if (!isObject(maybeServerVariableObject)) return false; + const typeofDefault = typeof (maybeServerVariableObject as Dictionary).default; return typeofDefault === 'string' || typeofDefault === 'boolean' || typeofDefault === 'number'; }; export const isTagObject = (maybeTagObject: unknown): maybeTagObject is TagObject => { - if (isPlainObject(maybeTagObject) && 'name' in maybeTagObject) { - return typeof maybeTagObject.name === 'string'; + if (isObject(maybeTagObject) && 'name' in maybeTagObject) { + return typeof (maybeTagObject as TagObject).name === 'string'; } return false; }; export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObject is ResponseObject => - isPlainObject(maybeResponseObject) && + isObject(maybeResponseObject) && ('description' in maybeResponseObject || 'headers' in maybeResponseObject || 'content' in maybeResponseObject || 'links' in maybeResponseObject); export const isOAuthFlowObject = (maybeOAuthFlowObject: unknown): maybeOAuthFlowObject is OAuthFlowObject => - isPlainObject(maybeOAuthFlowObject) && isPlainObject(maybeOAuthFlowObject.scopes); + isDictionary(maybeOAuthFlowObject) && isDictionary(maybeOAuthFlowObject.scopes); export const isRequestBodyObject = (maybeRequestBodyObject: unknown): maybeRequestBodyObject is RequestBodyObject => - isPlainObject(maybeRequestBodyObject) && isPlainObject(maybeRequestBodyObject.content); + isDictionary(maybeRequestBodyObject) && isDictionary(maybeRequestBodyObject.content); diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index f423a26c..28617ada 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,14 +1,13 @@ import { IHttpOperation } from '@stoplight/types'; -import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; -import pickBy = require('lodash.pickby'); +import { get, isNil, omitBy } from 'lodash'; +import type { OpenAPIObject, OperationObject, ParameterObject, PathsObject, RequestBodyObject } from 'openapi3-ts'; -import { createContext } from '../context'; -import { isNonNullable } from '../guards'; import { transformOasOperations } from '../oas'; -import { getExtensions } from '../oas/accessors'; -import { translateToTags } from '../oas/tags'; -import type { Oas3HttpOperationTransformer } from '../oas/types'; +import { getExtensions, getOasTags, getValidOasParameters } from '../oas/accessors'; +import { translateToTags } from '../oas/tag'; +import { Oas3HttpOperationTransformer } from '../oas/types'; import { maybeResolveLocalRef } from '../utils'; +import { isServerObject } from './guards'; import { translateToCallbacks } from './transformers/callbacks'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; @@ -20,7 +19,7 @@ export function transformOas3Operations(document: OpenAPIObject): IHttpOperation } export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]) as PathsObject; + const pathObj = maybeResolveLocalRef(document, get(document, ['paths', path])) as PathsObject; if (typeof pathObj !== 'object' || pathObj === null) { throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); } @@ -30,7 +29,7 @@ export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); } - const ctx = createContext(document); + const servers = operation.servers || pathObj.servers || document.servers; const httpOperation: IHttpOperation = { id: '?http-operation-id?', @@ -41,14 +40,18 @@ export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, method, path, summary: operation.summary, - responses: translateToResponses.call(ctx, operation.responses), - request: translateToRequest.call(ctx, pathObj, operation), - callbacks: translateToCallbacks.call(ctx, operation.callbacks), - tags: translateToTags.call(ctx, operation.tags), - security: translateToSecurities.call(ctx, operation.security), + responses: translateToResponses(document, operation.responses), + servers: Array.isArray(servers) ? translateToServers(servers.filter(isServerObject)) : [], + request: translateToRequest( + document, + getValidOasParameters(document, operation.parameters as ParameterObject[], pathObj.parameters), + operation.requestBody as RequestBodyObject, + ), + callbacks: operation.callbacks && translateToCallbacks(operation.callbacks), + tags: translateToTags(getOasTags(operation.tags)), + security: translateToSecurities(document, operation.security), extensions: getExtensions(operation), - servers: translateToServers.call(ctx, pathObj, operation), }; - return pickBy(httpOperation, isNonNullable) as IHttpOperation; + return omitBy(httpOperation, isNil) as IHttpOperation; }; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index acce098f..9626116e 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,27 +1,34 @@ -import { isPlainObject } from '@stoplight/json'; -import type { Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; -import { isNonNullable } from '../guards'; -import { transformOasService } from '../oas/service'; -import type { Oas3HttpServiceTransformer } from '../oas/types'; -import { ArrayCallbackParameters } from '../types'; -import { entries } from '../utils'; -import { SecurityWithKey } from './accessors'; -import { isSecurityScheme } from './guards'; -import { translateToSingleSecurity } from './transformers/securities'; -import { translateToServer } from './transformers/servers'; -import { Oas3TranslateFunction } from './types'; +import { HttpSecurityScheme, IHttpService, IServer } from '@stoplight/types'; +import { compact, filter, flatMap, keys, map, pickBy } from 'lodash'; + +import { hasXLogo } from '../oas/guards'; +import { translateLogo } from '../oas/transformers/translateLogo'; +import { Oas3HttpServiceTransformer } from '../oas/types'; +import { isDictionary } from '../utils'; +import { isSecurityScheme, isTagObject } from './guards'; +import { transformToSingleSecurity } from './transformers/securities'; +import { translateServerVariables } from './transformers/servers'; export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document); - const httpService = transformOasService.call(ctx); + const httpService: IHttpService = { + id: '?http-service-id?', + version: document.info?.version ?? '', + name: document.info?.title ?? 'no-title', + }; + + if (typeof document.info?.description === 'string') { + httpService.description = document.info.description; + } if (typeof document.info?.summary === 'string') { httpService.summary = document.info.summary; } - if (document.info?.license) { + if (document.info?.contact) { + httpService.contact = document.info.contact; + } + + if (typeof document.info?.license === 'object' && document.info.license !== null) { const { name, identifier, ...license } = document.info.license; httpService.license = { ...license, @@ -30,27 +37,50 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = }; } - const servers = Array.isArray(document.servers) - ? document.servers.map(translateToServer, ctx).filter(isNonNullable) - : []; + if (document.info?.termsOfService) { + httpService.termsOfService = document.info.termsOfService; + } + + if (isDictionary(document.info) && hasXLogo(document.info)) { + httpService.logo = translateLogo(document.info); + } + + const servers = compact( + map(document.servers, server => { + if (!server) return null; + const serv: IServer = { + name: document.info?.title ?? '', + description: server.description, + url: server.url ?? '', + }; + + const variables = server.variables && translateServerVariables(server.variables); + if (variables && Object.keys(variables).length) serv.variables = variables; + + return serv; + }), + ); if (servers.length) { httpService.servers = servers; } - const securitySchemes = entries(document.components?.securitySchemes) - .map(translateSecurityScheme, ctx) - .filter(isNonNullable); + const securitySchemes = compact( + keys(document.components?.securitySchemes).map(key => { + const definition = document?.components?.securitySchemes?.[key]; + return isSecurityScheme(definition) && transformToSingleSecurity(definition, key); + }), + ); if (securitySchemes.length) { httpService.securitySchemes = securitySchemes; } - const security = (Array.isArray(document.security) ? document.security : []) - .flatMap(sec => { - if (!isPlainObject(sec)) return null; + const security = compact( + flatMap(document.security, sec => { + if (!sec) return null; - return Object.keys(sec).map(key => { + return keys(sec).map(key => { const ss = securitySchemes.find(securityScheme => securityScheme.key === key); if (ss && ss.type === 'oauth2') { const flows = {}; @@ -74,26 +104,18 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = return ss; }); - }) - .filter(isNonNullable); + }), + ); if (security.length) { + //@ts-ignore I hate doing this, but unfortunately Lodash types are (rightfully) very loose and put undefined when it can't happen httpService.security = security; } - return httpService; -}; - -const translateSecurityScheme: Oas3TranslateFunction< - ArrayCallbackParameters<[name: string, scheme: unknown]>, - Optional -> = function ([key, definition]) { - if (!isSecurityScheme(definition)) return; - - const transformed = translateToSingleSecurity.call(this, definition); - if (transformed && 'key' in transformed) { - transformed.key = key; + const tags = filter(document.tags, isTagObject); + if (tags.length) { + httpService.tags = tags; } - return transformed; + return httpService; }; diff --git a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap index 04e8e103..22c29f5d 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap @@ -4,12 +4,12 @@ exports[`translateHeaderObject should translate to IHttpHeaderParam 1`] = ` Object { "allowEmptyValue": true, "allowReserved": true, - "content": Object {}, "deprecated": true, "description": "descr", "encodings": Array [], "examples": Array [ Object { + "description": undefined, "key": "a", "summary": "example summary", "value": "hey", @@ -22,10 +22,8 @@ Object { "explode": true, "name": "header-name", "required": true, - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, - "style": "simple", + "schema": Object {}, + "style": "matrix", } `; @@ -33,24 +31,21 @@ exports[`translateMediaTypeObject given complex nested media type object should Object { "encodings": Array [ Object { - "explode": true, + "contentType": "text/plain", "headers": Array [ Object { - "content": Object { - "nested/media": Object {}, - }, "description": "descr", "encodings": Array [], "examples": Array [ Object { + "description": undefined, "key": "a", "summary": "example summary", "value": "hey", }, ], - "explode": false, "name": "h1", - "style": "simple", + "style": "matrix", }, ], "mediaType": "text/plain", @@ -60,6 +55,7 @@ Object { ], "examples": Array [ Object { + "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -72,11 +68,40 @@ Object { } `; +exports[`translateMediaTypeObject given complex nested media type object with nullish headers should translate correctly 1`] = ` +Object { + "encodings": Array [ + Object { + "contentType": "text/plain", + "headers": Array [], + "mediaType": "text/plain", + "property": "enc1", + "style": "form", + }, + ], + "examples": Array [ + Object { + "description": undefined, + "key": "example", + "summary": "multi example", + "value": "hey", + }, + ], + "mediaType": "mediaType", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, +} +`; + +exports[`translateMediaTypeObject given encoding with incorrect style should throw an error 1`] = `"Encoding property style: 'xyz' is incorrect, must be one of: form,spaceDelimited,pipeDelimited,deepObject"`; + exports[`translateMediaTypeObject given encodings should translate each encoding to array item 1`] = ` Object { "encodings": Array [ Object { "allowReserved": true, + "contentType": "text/plain", "explode": true, "headers": Array [ Object { @@ -92,6 +117,7 @@ Object { }, Object { "allowReserved": true, + "contentType": "text/plain", "explode": true, "headers": Array [ Object { @@ -108,6 +134,7 @@ Object { ], "examples": Array [ Object { + "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -125,6 +152,7 @@ Object { "encodings": Array [], "examples": Array [ Object { + "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -152,28 +180,3 @@ Object { }, } `; - -exports[`translateMediaTypeObject should skip nullish headers 1`] = ` -Object { - "encodings": Array [ - Object { - "explode": true, - "headers": Array [], - "mediaType": "text/plain", - "property": "enc1", - "style": "form", - }, - ], - "examples": Array [ - Object { - "key": "example", - "summary": "multi example", - "value": "hey", - }, - ], - "mediaType": "mediaType", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, -} -`; diff --git a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap index 4afeb24b..164c80f4 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -4,7 +4,7 @@ exports[`translateOas3ToRequest give a request body should translate it 1`] = ` Object { "body": Object { "contents": Array [ - "translateMediaTypeObject([\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}], 0, [[\\"content-a\\",{\\"schema\\":{\\"deprecated\\":true}}]])", + "translateMediaTypeObject({}, {\\"schema\\":{\\"deprecated\\":true}}, content-a, {\\"content-a\\":{\\"schema\\":{\\"deprecated\\":true}}})", ], "description": "descr", "required": true, @@ -29,41 +29,33 @@ Object { "schema": Object {}, }, }, - "deprecated": false, "description": "descr", "examples": Array [], - "explode": false, "name": "param-name-3", - "style": "simple", }, ], "path": Array [], "query": Array [ Object { "content": Object { - "content-b": Object { + "content-a": Object { "schema": Object {}, }, }, - "deprecated": false, + "deprecated": true, "description": "descr", "examples": Array [], - "explode": false, - "name": "param-name-2", - "style": "simple", + "name": "param-name-1", }, Object { "content": Object { - "content-a": Object { + "content-b": Object { "schema": Object {}, }, }, - "deprecated": true, "description": "descr", "examples": Array [], - "explode": false, - "name": "param-name-1", - "style": "simple", + "name": "param-name-2", }, ], } diff --git a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap index 54946b4f..90fa85a6 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -14,6 +14,7 @@ Array [ }, ], "mediaType": "fake-content-type-200", + "schema": undefined, }, ], "description": "descr 200", @@ -33,6 +34,7 @@ Array [ "encodings": Array [], "examples": Array [], "mediaType": "fake-content-type", + "schema": undefined, }, ], "description": "descr", @@ -46,13 +48,9 @@ Array [ "value": 1000, }, ], - "explode": false, "name": "fake-header-name-1", "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, "type": "integer", }, "style": "simple", @@ -66,14 +64,10 @@ Array [ "value": 1000, }, ], - "explode": false, "name": "fake-header-name-2", "required": true, "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, "type": "integer", }, "style": "simple", diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index d22bf44a..05fef5b7 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -1,16 +1,6 @@ -import type { SchemaObject } from 'openapi3-ts'; +import { SchemaObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; -import { - translateHeaderObject as _translateHeaderObject, - translateMediaTypeObject as _translateMediaTypeObject, -} from '../content'; - -const translateMediaTypeObject = (document: any, object: unknown, key: string) => - _translateMediaTypeObject.call(createContext(document), [key, object], 0, []); - -const translateHeaderObject = (object: unknown, key: string) => - _translateHeaderObject.call(createContext({}), [key, object], 0, []); +import { translateHeaderObject, translateMediaTypeObject } from '../content'; describe('translateMediaTypeObject', () => { afterEach(() => { @@ -26,6 +16,28 @@ describe('translateMediaTypeObject', () => { encodings: [], examples: [], mediaType: 'mediaType', + schema: void 0, + }); + }); + + it('given invalid schema, should return nothing', () => { + expect( + translateMediaTypeObject( + {}, + { + schema: { + get properties() { + throw new Error('I am invalid'); + }, + }, + }, + 'mediaType', + ), + ).toStrictEqual({ + encodings: [], + examples: [], + mediaType: 'mediaType', + schema: void 0, }); }); @@ -42,6 +54,7 @@ describe('translateMediaTypeObject', () => { encodings: [], examples: [], mediaType: 'mediaType', + schema: void 0, }); }); @@ -146,7 +159,7 @@ describe('translateMediaTypeObject', () => { ).toMatchSnapshot(); }); - it('should skip nullish headers', () => { + it('given complex nested media type object with nullish headers should translate correctly', () => { expect( translateMediaTypeObject( {}, @@ -168,6 +181,26 @@ describe('translateMediaTypeObject', () => { ).toMatchSnapshot(); }); + it('given encoding with incorrect style should throw an error', () => { + const testedFunction = () => { + translateMediaTypeObject( + {}, + { + schema: {}, + examples: { example: { summary: 'multi example' } }, + encoding: { + enc1: { + contentType: 'text/plain', + style: 'xyz', + }, + }, + }, + 'mediaType', + ); + }; + expect(testedFunction).toThrowErrorMatchingSnapshot(); + }); + it('given encoding with no style it should not throw an error', () => { const testedFunction = () => { translateMediaTypeObject( diff --git a/src/oas3/transformers/__tests__/request.test.ts b/src/oas3/transformers/__tests__/request.test.ts index 0be85e70..bc7bc201 100644 --- a/src/oas3/transformers/__tests__/request.test.ts +++ b/src/oas3/transformers/__tests__/request.test.ts @@ -1,70 +1,56 @@ import { mockPassthroughImplementation } from '@stoplight/test-utils'; -import { createContext } from '../../../context'; import { translateMediaTypeObject } from '../content'; -import { translateToRequest as _translateToRequest } from '../request'; +import { translateToRequest } from '../request'; jest.mock('../content'); -const translateToRequest = (path: Record, operation: Record) => { - const ctx = createContext({ paths: { '/api': path } }); - return _translateToRequest.call(ctx, path, operation); -}; - describe('translateOas3ToRequest', () => { beforeEach(() => { mockPassthroughImplementation(translateMediaTypeObject); }); it('given no request body should translate parameters', () => { - const operation = { - parameters: [ + expect( + translateToRequest({}, [ { - name: 'param-name-2', + name: 'param-name-1', in: 'query', description: 'descr', + deprecated: true, content: { - 'content-b': { + 'content-a': { schema: {}, }, }, }, { - name: 'param-name-3', - in: 'header', + name: 'param-name-2', + in: 'query', description: 'descr', content: { - 'content-c': { + 'content-b': { schema: {}, }, }, }, - ], - }; - - const path = { - parameters: [ { - name: 'param-name-1', - in: 'query', + name: 'param-name-3', + in: 'header', description: 'descr', - deprecated: true, content: { - 'content-a': { + 'content-c': { schema: {}, }, }, }, - ], - get: operation, - }; - - expect(translateToRequest(path, operation)).toMatchSnapshot(); + ]), + ).toMatchSnapshot(); }); it('give a request body should translate it', () => { - const operation = { - requestBody: { + expect( + translateToRequest({}, [], { description: 'descr', required: true, content: { @@ -74,13 +60,7 @@ describe('translateOas3ToRequest', () => { }, }, }, - }, - }; - - const path = { - post: operation, - }; - - expect(translateToRequest(path, operation)).toMatchSnapshot(); + }), + ).toMatchSnapshot(); }); }); diff --git a/src/oas3/transformers/__tests__/responses.test.ts b/src/oas3/transformers/__tests__/responses.test.ts index 9f5a929d..0d8154fd 100644 --- a/src/oas3/transformers/__tests__/responses.test.ts +++ b/src/oas3/transformers/__tests__/responses.test.ts @@ -1,11 +1,7 @@ import type { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; -import { translateToResponses as _translateToResponses } from '../responses'; - -const translateToResponses = (document: DeepPartial, responses: unknown) => - _translateToResponses.call(createContext(document), responses); +import { translateToResponses } from '../responses'; describe('translateToOas3Responses', () => { it('given empty dictionary should return empty array', () => { @@ -74,6 +70,7 @@ describe('translateToOas3Responses', () => { { code: '200', contents: [], + description: void 0, headers: [], }, ]); @@ -215,12 +212,10 @@ describe('translateToOas3Responses', () => { { name: 'X-Page', schema: { - $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', }, style: 'simple', description: 'Current page (if pagination parameters were provided in the request)', - explode: false, encodings: [], examples: [ { diff --git a/src/oas3/transformers/__tests__/securities.test.ts b/src/oas3/transformers/__tests__/securities.test.ts index b682c291..0bf47a08 100644 --- a/src/oas3/transformers/__tests__/securities.test.ts +++ b/src/oas3/transformers/__tests__/securities.test.ts @@ -1,12 +1,6 @@ -import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; -import { OperationSecurities } from '../../accessors'; -import { translateToSecurities as _translateToSecurities } from '../securities'; - -const translateToSecurities = (document: DeepPartial, operationSecurities: OperationSecurities) => - _translateToSecurities.call(createContext(document), operationSecurities); +import { translateToSecurities } from '../securities'; describe('securities', () => { describe('translateToSecurities', () => { diff --git a/src/oas3/transformers/__tests__/servers.test.ts b/src/oas3/transformers/__tests__/servers.test.ts index d6573d06..3e396d77 100644 --- a/src/oas3/transformers/__tests__/servers.test.ts +++ b/src/oas3/transformers/__tests__/servers.test.ts @@ -1,17 +1,9 @@ -import { DeepPartial } from '@stoplight/types'; -import { OpenAPIObject } from 'openapi3-ts'; - -import { createContext } from '../../../context'; -import { translateToServers as _translateToServers } from '../servers'; - -const translateToServers = ( - document: DeepPartial & { paths: { '/pet': { get: Record } } }, -) => _translateToServers.call(createContext(document), document.paths['/pet'], document.paths['/pet'].get); +import { translateToServers } from '../servers'; describe('translateToServers', () => { it('translate single ServerObject to IServer', () => { - const document = { - servers: [ + expect( + translateToServers([ { description: 'description', url: 'http://stoplight.io/path', @@ -28,20 +20,13 @@ describe('translateToServers', () => { }, }, }, - ], - paths: { - '/pet': { - get: {}, - }, - }, - }; - - expect(translateToServers(document)).toMatchSnapshot(); + ]), + ).toMatchSnapshot(); }); it('filters out invalid variables', () => { - const document = { - servers: [ + expect( + translateToServers([ { description: 'description', url: 'http://stoplight.io/path', @@ -58,15 +43,8 @@ describe('translateToServers', () => { }, } as any, }, - ], - paths: { - '/pet': { - get: {}, - }, - }, - }; - - expect(translateToServers(document)).toStrictEqual([ + ]), + ).toStrictEqual([ { description: 'description', url: 'http://stoplight.io/path', @@ -80,71 +58,4 @@ describe('translateToServers', () => { }, ]); }); - - it('prefers operation servers over any other servers', () => { - const document = { - servers: [ - { - description: 'description', - url: 'http://stoplight.io/path', - }, - ], - paths: { - '/pet': { - servers: [ - { - description: 'description', - url: 'http://stoplight.io/pet', - }, - ], - - get: { - servers: [ - { - description: 'description', - url: 'http://stoplight.io/pet.get', - }, - ], - }, - }, - }, - }; - - expect(translateToServers(document)).toStrictEqual([ - { - description: 'description', - url: 'http://stoplight.io/pet.get', - }, - ]); - }); - - it('given missing operation servers, prefers path servers over any document servers', () => { - const document = { - servers: [ - { - description: 'description', - url: 'http://stoplight.io/path', - }, - ], - paths: { - '/pet': { - servers: [ - { - description: 'description', - url: 'http://stoplight.io/pet', - }, - ], - - get: {}, - }, - }, - }; - - expect(translateToServers(document)).toStrictEqual([ - { - description: 'description', - url: 'http://stoplight.io/pet', - }, - ]); - }); }); diff --git a/src/oas3/transformers/callbacks.ts b/src/oas3/transformers/callbacks.ts index 422cdbd8..c97e4bf5 100644 --- a/src/oas3/transformers/callbacks.ts +++ b/src/oas3/transformers/callbacks.ts @@ -1,35 +1,33 @@ -import type { IHttpCallbackOperation } from '@stoplight/types'; -import type { OpenAPIObject } from 'openapi3-ts'; +import { IHttpCallbackOperation } from '@stoplight/types'; +import { entries } from 'lodash'; +import { CallbacksObject, OpenAPIObject } from 'openapi3-ts'; -import { entries } from '../../utils'; import { transformOas3Operation } from '../operation'; -import type { Oas3TranslateFunction } from '../types'; -export const translateToCallbacks: Oas3TranslateFunction<[callbacks: unknown], IHttpCallbackOperation[] | undefined> = - function (callbacks) { - const callbackEntries = entries(callbacks); - if (!callbackEntries.length) return; +export function translateToCallbacks(callbacks: CallbacksObject): IHttpCallbackOperation[] | undefined { + const callbackEntries = entries(callbacks); + if (!callbackEntries.length) return; - return callbackEntries.reduce((results: IHttpCallbackOperation[], [callbackName, path2Methods]) => { - for (const [path, method2Op] of entries(path2Methods)) { - for (const [method, op] of entries(method2Op as { [key: string]: {} })) { - const document: Partial = { - openapi: '3', - info: { title: '', version: '1' }, - paths: { [path]: { [method]: op } }, - }; + return callbackEntries.reduce((results: IHttpCallbackOperation[], [callbackName, path2Methods]) => { + for (const [path, method2Op] of entries(path2Methods)) { + for (const [method, op] of entries(method2Op as { [key: string]: {} })) { + const document: Partial = { + openapi: '3', + info: { title: '', version: '1' }, + paths: { [path]: { [method]: op } }, + }; - results.push({ - ...transformOas3Operation({ - document, - method, - path, - }), - callbackName, - }); - } + results.push({ + ...transformOas3Operation({ + document, + method, + path, + }), + callbackName, + }); } + } - return results; - }, []); - }; + return results; + }, []); +} diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index 7b33152f..696e31a1 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -1,5 +1,5 @@ -import { isPlainObject } from '@stoplight/json'; import { + DeepPartial, Dictionary, HttpParamStyles, IHttpEncoding, @@ -8,69 +8,48 @@ import { INodeExample, Optional, } from '@stoplight/types'; -import type { JSONSchema7 } from 'json-schema'; -import pickBy = require('lodash.pickby'); +import { JSONSchema7 } from 'json-schema'; +import { compact, each, get, isObject, keys, map, omit, pickBy, union, values } from 'lodash'; +import { EncodingPropertyObject, HeaderObject, MediaTypeObject, OpenAPIObject } from 'openapi3-ts'; -import { isBoolean, isNonNullable, isString } from '../../guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { ArrayCallbackParameters, Fragment } from '../../types'; -import { entries, maybeResolveLocalRef } from '../../utils'; +import { isDictionary, maybeResolveLocalRef } from '../../utils'; import { isHeaderObject } from '../guards'; -import { Oas3TranslateFunction } from '../types'; - -const ACCEPTABLE_STYLES: (string | undefined)[] = [ - HttpParamStyles.Form, - HttpParamStyles.SpaceDelimited, - HttpParamStyles.PipeDelimited, - HttpParamStyles.DeepObject, -]; - -function isAcceptableStyle( - encodingPropertyObject: T, -): encodingPropertyObject is T & { - style: - | HttpParamStyles.Form - | HttpParamStyles.SpaceDelimited - | HttpParamStyles.PipeDelimited - | HttpParamStyles.DeepObject; -} { - return typeof encodingPropertyObject.style === 'string' && ACCEPTABLE_STYLES.includes(encodingPropertyObject.style); -} -const translateEncodingPropertyObject: Oas3TranslateFunction< - ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, - Optional -> = function ([property, encodingPropertyObject]) { - if (!isPlainObject(encodingPropertyObject)) return; - if (!isAcceptableStyle(encodingPropertyObject)) return; +function translateEncodingPropertyObject( + encodingPropertyObject: EncodingPropertyObject, + property: string, +): IHttpEncoding { + const acceptableStyles: (string | undefined)[] = [ + HttpParamStyles.Form, + HttpParamStyles.SpaceDelimited, + HttpParamStyles.PipeDelimited, + HttpParamStyles.DeepObject, + ]; + + if (encodingPropertyObject.style && !acceptableStyles.includes(encodingPropertyObject.style)) { + throw new Error( + `Encoding property style: '${encodingPropertyObject.style}' is incorrect, must be one of: ${acceptableStyles}`, + ); + } return { property, - explode: !!(encodingPropertyObject.explode ?? encodingPropertyObject.style === HttpParamStyles.Form), - style: encodingPropertyObject.style, - headers: entries(encodingPropertyObject.headers).map(translateHeaderObject, this).filter(isNonNullable), - - ...pickBy( - { - allowReserved: encodingPropertyObject.allowReserved, - }, - isBoolean, - ), - - ...pickBy( - { - mediaType: encodingPropertyObject.contentType, - }, - isString, + ...encodingPropertyObject, + // workaround for 'style' being one of the accepted HttpParamStyles + style: encodingPropertyObject.style as any, + mediaType: encodingPropertyObject.contentType, + headers: compact( + map & unknown, Optional>( + encodingPropertyObject.headers, + translateHeaderObject, + ), ), }; -}; +} -export const translateHeaderObject = < - Oas3TranslateFunction, Optional> ->function ([name, unresolvedHeaderObject]) { - const headerObject = this.maybeResolveLocalRef(unresolvedHeaderObject); - if (!isPlainObject(headerObject)) return; +export function translateHeaderObject(headerObject: unknown, name: string): Optional { + if (!isObject(headerObject)) return; if (!isHeaderObject(headerObject)) { return { @@ -83,150 +62,116 @@ export const translateHeaderObject = < const { content: contentObject } = headerObject; - const contentValue = isPlainObject(contentObject) ? Object.values(contentObject)[0] : null; + const contentValue = values(contentObject)[0]; - const baseContent: IHttpHeaderParam = { + const baseContent = { + // TODO(SL-249): we are missing examples in our types, on purpose? + // examples: parameterObject.examples, + ...omit(headerObject, 'content', 'style', 'examples', 'example', 'schema'), name, - style: HttpParamStyles.Simple, - explode: !!headerObject.explode, - - ...pickBy( - { - schema: isPlainObject(headerObject.schema) ? translateSchemaObject.call(this, headerObject.schema) : null, - content: headerObject.content, - }, - isNonNullable, - ), - - ...pickBy( - { - description: headerObject.description, - }, - isString, - ), - - ...pickBy( - { - allowEmptyValue: headerObject.allowEmptyValue, - allowReserved: headerObject.allowReserved, - - required: headerObject.required, - deprecated: headerObject.deprecated, - }, - isBoolean, - ), + style: headerObject?.style ?? HttpParamStyles.Simple, }; const examples: INodeExample[] = []; const encodings: IHttpEncoding[] = []; - if (isPlainObject(contentValue)) { - examples.push(...entries(contentValue.examples).map(translateToExample, this).filter(isNonNullable)); + if (contentValue) { + examples.push(...keys(contentValue.examples).map(transformExamples(contentValue))); - if (isPlainObject(contentValue.encoding)) { - encodings.push(...(Object.values(contentValue.encoding) as IHttpEncoding[])); - } + encodings.push(...values(contentValue.encoding)); - if ('example' in contentValue) { - examples.push(transformDefaultExample.call(this, '__default_content', contentValue.example)); + if (contentValue.example) { + examples.push({ + key: '__default_content', + value: contentValue.example, + }); } } - examples.push(...entries(headerObject.examples).map(translateToExample, this).filter(isNonNullable)); + examples.push(...keys(headerObject.examples).map(transformExamples(headerObject))); - if ('example' in headerObject) { - examples.push(transformDefaultExample.call(this, '__default', headerObject.example)); + if (headerObject.example) { + examples.push({ + key: '__default', + value: headerObject.example, + }); } - return { + return pickBy({ ...baseContent, + schema: get(headerObject, 'schema') as any, encodings, examples, - }; -}; - -const translateSchemaMediaTypeObject: Oas3TranslateFunction<[schema: unknown], Optional> = function ( - schema, -) { - if (!isPlainObject(schema)) return; - - return translateSchemaObject.call(this, schema); -}; + }) as unknown as IHttpHeaderParam; +} -export const translateMediaTypeObject: Oas3TranslateFunction< - ArrayCallbackParameters<[mediaType: string, mediaObject: unknown]>, - Optional -> = function ([mediaType, mediaObject]) { - if (!isPlainObject(mediaObject)) return; +export function translateMediaTypeObject( + document: DeepPartial, + mediaObject: unknown, + mediaType: string, +): Optional { + if (!isDictionary(mediaObject)) return; - const resolvedMediaObject = resolveMediaObject(this.document, mediaObject); + const resolvedMediaObject = resolveMediaObject(document, mediaObject); const { schema, encoding, examples } = resolvedMediaObject; - const jsonSchema = translateSchemaMediaTypeObject.call(this, schema); + let jsonSchema: Optional; + + if (isObject(schema)) { + try { + jsonSchema = translateSchemaObject(document, schema); + } catch { + // happens + } + } const example = resolvedMediaObject.example || jsonSchema?.examples?.[0]; return { mediaType, + schema: jsonSchema, // Note that I'm assuming all references are resolved - examples: [ - example ? transformDefaultExample.call(this, 'default', example) : undefined, - ...entries(examples).map(translateToExample, this), - ].filter(isNonNullable), - encodings: entries(encoding).map(translateEncodingPropertyObject, this).filter(isNonNullable), - - ...pickBy( - { - schema: jsonSchema, - }, - isNonNullable, + examples: compact( + union( + example ? [{ key: 'default', value: example }] : undefined, + isDictionary(examples) + ? Object.keys(examples).map(exampleKey => ({ + key: exampleKey, + summary: get(examples, [exampleKey, 'summary']), + description: get(examples, [exampleKey, 'description']), + value: get(examples, [exampleKey, 'value']), + })) + : [], + ), ), + encodings: map(encoding, translateEncodingPropertyObject), }; -}; +} -function resolveMediaObject(document: unknown, maybeMediaObject: Dictionary) { +function resolveMediaObject(document: DeepPartial, maybeMediaObject: Dictionary) { const mediaObject = { ...maybeMediaObject }; - if (isPlainObject(mediaObject.schema)) { + if (isDictionary(mediaObject.schema)) { mediaObject.schema = maybeResolveLocalRef(document, mediaObject.schema); } - if (isPlainObject(mediaObject.examples)) { + if (isDictionary(mediaObject.examples)) { const examples = { ...mediaObject.examples }; mediaObject.examples = examples; - for (const [exampleName, exampleValue] of entries(examples)) { + each(examples, (exampleValue, exampleName) => { examples[exampleName] = maybeResolveLocalRef(document, exampleValue); - } + }); } return mediaObject; } -const transformDefaultExample: Oas3TranslateFunction<[key: string, value: unknown], INodeExample> = function ( - key, - value, -) { - return { - value, - key, - }; -}; - -const translateToExample: Oas3TranslateFunction< - ArrayCallbackParameters<[key: string, example: unknown]>, - Optional -> = function ([key, example]) { - if (!isPlainObject(example)) return; - - return { - value: example.value, - key, - - ...pickBy( - { - summary: example.summary, - description: example.description, - }, - isString, - ), +const transformExamples = + (source: MediaTypeObject | HeaderObject) => + (key: string): INodeExample => { + return { + summary: get(source, ['examples', key, 'summary']), + description: get(source, ['examples', key, 'description']), + value: get(source, ['examples', key, 'value']), + key, + }; }; -}; diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index f7997316..b0e99d27 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -1,143 +1,107 @@ -import { isPlainObject } from '@stoplight/json'; import type { + DeepPartial, + Dictionary, + IHttpCookieParam, IHttpHeaderParam, IHttpOperationRequest, IHttpOperationRequestBody, IHttpParam, - INodeExample, - INodeExternalExample, + IHttpPathParam, + IHttpQueryParam, + IMediaTypeContent, Optional, } from '@stoplight/types'; -import { HttpParamStyles } from '@stoplight/types'; -import type { JSONSchema7 } from 'json-schema'; -import type { ParameterObject } from 'openapi3-ts'; -import pickBy = require('lodash.pickby'); +import { compact, map, omit, partial, pickBy } from 'lodash'; +import type { OpenAPIObject, ParameterObject, RequestBodyObject } from 'openapi3-ts'; -import { isNonNullable, isString } from '../../guards'; -import { OasVersion } from '../../oas'; -import { getValidOasParameters } from '../../oas/accessors'; -import { isValidParamStyle } from '../../oas/guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import type { ArrayCallbackParameters, Fragment } from '../../types'; -import { entries } from '../../utils'; +import { isDictionary, maybeResolveLocalRef } from '../../utils'; import { isRequestBodyObject } from '../guards'; -import type { Oas3TranslateFunction } from '../types'; import { translateMediaTypeObject } from './content'; -export const translateRequestBody: Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> = - function (requestBodyObject) { - const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); - - if (isRequestBodyObject(resolvedRequestBodyObject)) { - return { - contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), - - ...pickBy( - { - required: resolvedRequestBodyObject.required, - description: resolvedRequestBodyObject.description, - }, - isNonNullable, - ), - }; - } - - return { contents: [] }; - }; - -const translateParameterObjectSchema: Oas3TranslateFunction< - [parameterObject: Fragment], - Optional -> = function (parameterObject) { - if (!isPlainObject(parameterObject.schema)) return; - - return translateSchemaObject.call(this, { - ...parameterObject.schema, - ...('example' in parameterObject ? { example: parameterObject.example } : null), - }); -}; - -const translateToExample: Oas3TranslateFunction< - ArrayCallbackParameters<[key: string, example: unknown]>, - Optional -> = function ([key, example]) { - if (!isPlainObject(example)) return; - - if (!('value' in example) && typeof example.externalValue !== 'string') return; - +function translateRequestBody( + document: DeepPartial, + requestBodyObject: RequestBodyObject, +): IHttpOperationRequestBody { return { - key, - - ...(typeof example.externalValue === 'string' - ? { externalValue: example.externalValue } - : { value: example.value }), - - ...pickBy( - { - summary: example.summary, - description: example.description, - }, - isString, + required: requestBodyObject.required, + description: requestBodyObject.description, + contents: compact( + map & unknown, Optional>( + requestBodyObject.content, + partial(translateMediaTypeObject, document), + ), ), }; -}; - -export const translateParameterObject: Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> = - function (parameterObject) { - const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); +} - const hasDefaultExample = examples.some(({ key }) => key.includes('default')); - const schema = translateParameterObjectSchema.call(this, parameterObject); - - return { - name: parameterObject.name, - deprecated: !!parameterObject.deprecated, - style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, - explode: !!(parameterObject.explode ?? parameterObject.style === HttpParamStyles.Form), - examples: - 'example' in parameterObject && !hasDefaultExample - ? [{ key: 'default', value: parameterObject.example }, ...examples] - : examples, - - ...pickBy( - { - description: parameterObject.description, - }, - isString, - ), - - ...pickBy( - { - schema, - content: parameterObject.content, - }, - isPlainObject, - ), - }; +export function translateParameterObject( + document: DeepPartial, + parameterObject: ParameterObject, +): IHttpParam | any { + const examples = map(parameterObject.examples, (example, key) => ({ + key, + ...example, + })); + + const hasDefaultExample = examples.map(({ key }) => key).includes('default'); + + return pickBy({ + ...omit(parameterObject, 'in', 'schema', 'example'), + name: parameterObject.name, + style: parameterObject.style, + schema: isDictionary(parameterObject.schema) + ? translateSchemaObject(document, { + ...parameterObject.schema, + ...('example' in parameterObject ? { example: parameterObject.example } : null), + }) + : void 0, + examples: + 'example' in parameterObject && !hasDefaultExample + ? [{ key: 'default', value: parameterObject.example }, ...examples] + : examples, + }); +} + +export function translateToRequest( + document: DeepPartial, + parameters: ParameterObject[], + requestBodyObject?: RequestBodyObject, +): IHttpOperationRequest { + const params: { + header: IHttpHeaderParam[]; + query: IHttpQueryParam[]; + cookie: IHttpCookieParam[]; + path: IHttpPathParam[]; + } = { + header: [], + query: [], + cookie: [], + path: [], }; -export const translateToRequest: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IHttpOperationRequest> = - function (path, operation) { - const params: Omit & { header: IHttpHeaderParam[] } = { - header: [], - query: [], - cookie: [], - path: [], - }; + for (const parameter of parameters) { + const { in: key } = parameter; + if (!params.hasOwnProperty(key)) continue; - for (const param of getValidOasParameters(this.document, OasVersion.OAS3, operation.parameters, path.parameters)) { - const { in: key } = param; - const target = params[key]; - if (!Array.isArray(target)) continue; + params[key].push(translateParameterObject(document, parameter)); + } - target.push(translateParameterObject.call(this, param) as any); - } + let body; + if (isDictionary(requestBodyObject)) { + const resolvedRequestBodyObject = maybeResolveLocalRef(document, requestBodyObject) as RequestBodyObject; + body = isRequestBodyObject(resolvedRequestBodyObject) + ? translateRequestBody(document, resolvedRequestBodyObject) + : { contents: [] }; + } else { + body = { contents: [] }; + } - return { - body: translateRequestBody.call(this, operation?.requestBody), - headers: params.header, - query: params.query, - cookie: params.cookie, - path: params.path, - }; + return { + body, + headers: params.header, + query: params.query, + cookie: params.cookie, + path: params.path, }; +} diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index 2f7225f9..4a2e8742 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,36 +1,56 @@ -import type { IHttpOperationResponse, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import { + DeepPartial, + Dictionary, + IHttpHeaderParam, + IHttpOperationResponse, + IMediaTypeContent, + Optional, +} from '@stoplight/types'; +import { compact, map, partial, reduce } from 'lodash'; +import { OpenAPIObject } from 'openapi3-ts'; -import { isNonNullable, isString } from '../../guards'; -import type { ArrayCallbackParameters } from '../../types'; -import { entries } from '../../utils'; +import { isDictionary, maybeResolveLocalRef } from '../../utils'; import { isResponseObject } from '../guards'; -import type { Oas3TranslateFunction } from '../types'; import { translateHeaderObject, translateMediaTypeObject } from './content'; -const translateToResponse: Oas3TranslateFunction< - ArrayCallbackParameters<[statusCode: string, response: unknown]>, - Optional -> = function ([statusCode, response]) { - const resolvedResponse = this.maybeResolveLocalRef(response); +function translateToResponse( + document: DeepPartial, + response: unknown, + statusCode: string, +): Optional { + const resolvedResponse = maybeResolveLocalRef(document, response); if (!isResponseObject(resolvedResponse)) return; + const dereferencedHeaders = reduce( + resolvedResponse.headers, + (result, header, name) => { + return { ...result, [name]: maybeResolveLocalRef(document, header) }; + }, + {}, + ); + return { code: statusCode, - headers: entries(resolvedResponse.headers).map(translateHeaderObject, this).filter(isNonNullable), - contents: entries(resolvedResponse.content).map(translateMediaTypeObject, this).filter(isNonNullable), - - ...pickBy( - { - description: resolvedResponse.description, - }, - isString, + description: resolvedResponse.description, + headers: compact(map(dereferencedHeaders, translateHeaderObject)), + contents: compact( + map & unknown, Optional>( + resolvedResponse.content, + partial(translateMediaTypeObject, document), + ), ), }; -}; +} + +export function translateToResponses( + document: DeepPartial, + responses: unknown, +): IHttpOperationResponse[] { + if (!isDictionary(responses)) { + return []; + } -export const translateToResponses: Oas3TranslateFunction<[responses: unknown], IHttpOperationResponse[]> = function ( - responses, -) { - return entries(responses).map(translateToResponse, this).filter(isNonNullable); -}; + return compact( + map, Optional>(responses, partial(translateToResponse, document)), + ); +} diff --git a/src/oas3/transformers/securities.ts b/src/oas3/transformers/securities.ts index 53618e93..2f5d45e2 100644 --- a/src/oas3/transformers/securities.ts +++ b/src/oas3/transformers/securities.ts @@ -1,26 +1,29 @@ -import { isPlainObject } from '@stoplight/json'; -import type { IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; -import type { SecuritySchemeObject } from 'openapi3-ts'; +import { DeepPartial, IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; +import { OpenAPIObject, SecuritySchemeObject } from 'openapi3-ts'; -import { isNonNullable } from '../../guards'; -import { ArrayCallbackParameters } from '../../types'; -import { getSecurities, SecurityWithKey } from '../accessors'; +import { isDictionary } from '../../utils'; +import { getSecurities, OperationSecurities, SecurityWithKey } from '../accessors'; import { isOAuthFlowObject } from '../guards'; -import { Oas3TranslateFunction } from '../types'; -export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = - function (operationSecurities) { - const securities = getSecurities(this.document, operationSecurities); +export function translateToSecurities(document: DeepPartial, operationSecurities: OperationSecurities) { + const securities = getSecurities(document, operationSecurities); - return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); - }; + return securities.map(security => + security.reduce((transformedSecurities, sec) => { + const transformed = transformToSingleSecurity(sec, sec.key); + if (transformed) { + transformedSecurities.push(transformed); + } -export const translateToSingleSecurity: Oas3TranslateFunction< - [ArrayCallbackParameters & { type: 'mutualTLS' })>[0]], - Optional -> = function (securityScheme) { - const { key } = securityScheme; + return transformedSecurities; + }, []), + ); +} +export function transformToSingleSecurity( + securityScheme: SecuritySchemeObject | (Omit & { type: 'mutualTLS' }), + key: string, +): SecurityWithKey | undefined { const baseObject: { key: string; description?: string } = { key, }; @@ -79,12 +82,12 @@ export const translateToSingleSecurity: Oas3TranslateFunction< } return undefined; -}; +} function transformFlows(flows: Optional): IOauthFlowObjects { const transformedFlows: IOauthFlowObjects = {}; - if (!isPlainObject(flows)) { + if (!isDictionary(flows)) { return transformedFlows; } diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index 9a22e861..ae0963c1 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,89 +1,24 @@ -import type { INodeVariable, IServer, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import type { DeepPartial, Dictionary, INodeVariable, IServer } from '@stoplight/types'; +import { map, mapValues, pickBy } from 'lodash'; +import { ServerObject, ServerVariableObject } from 'openapi3-ts'; -import { isNonNullable, isString } from '../../guards'; -import { ArrayCallbackParameters, Fragment } from '../../types'; -import { entries } from '../../utils'; -import { isServerObject, isServerVariableObject } from '../guards'; -import { Oas3TranslateFunction } from '../types'; +import { isServerVariableObject } from '../guards'; -export const translateToServers: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IServer[]> = function ( - path, - operation, -) { - let servers; - if (Array.isArray(operation.servers)) { - servers = operation.servers; - } else if (Array.isArray(path.servers)) { - servers = path.servers; - } else if (Array.isArray(this.document.servers)) { - servers = this.document.servers; - } else { - return []; - } - - return servers.map(translateToServer, this).filter(isNonNullable); -}; - -export const translateToServer: Oas3TranslateFunction, Optional> = function ( - server, -) { - if (!isServerObject(server)) return; - - const variables = translateServerVariables.call(this, server.variables); - - return { +export function translateToServers(servers: ServerObject[]): IServer[] { + return servers.map(server => ({ + description: server.description, url: server.url, - - ...pickBy( - { - name: this.document.info?.title, - description: server.description, - }, - isString, - ), - - ...pickBy( - { - variables, - }, - isNonNullable, - ), - }; -}; - -export const translateServerVariables: Oas3TranslateFunction< - [variables: unknown], - Optional> -> = variables => { - const serverVariables = entries(variables).map(translateServerVariable).filter(isNonNullable); - return serverVariables.length > 0 ? Object.fromEntries(serverVariables) : undefined; -}; - -const translateServerVariable: Oas3TranslateFunction< - ArrayCallbackParameters<[name: string, variable: unknown]>, - Optional<[string, INodeVariable]> -> = function ([name, variable]) { - if (!isServerVariableObject(variable)) return; - - return [ - name, - { - default: String(variable.default), - - ...pickBy( - { - description: variable.description, - }, - isString, - ), - - ...pickBy( - { - enum: Array.isArray(variable.enum) ? variable.enum.map(String) : undefined, - }, - isNonNullable, - ), - }, - ]; -}; + variables: server.variables && translateServerVariables(server.variables), + })); +} + +export function translateServerVariables(variables: DeepPartial<{ [v: string]: ServerVariableObject }>) { + return mapValues, INodeVariable>( + pickBy(variables, isServerVariableObject), + value => ({ + default: String(value.default), + description: value.description && String(value.description), + enum: value.enum && map(value.enum, String), + }), + ); +} diff --git a/src/oas3/types.ts b/src/oas3/types.ts deleted file mode 100644 index c452b6b3..00000000 --- a/src/oas3/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DeepPartial } from '@stoplight/types'; -import { OpenAPIObject } from 'openapi3-ts'; - -import { TranslateFunction } from '../types'; - -export type Oas3TranslateFunction

= TranslateFunction< - DeepPartial, - P, - R ->; diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index fd51a78c..3efb81d4 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -255,7 +255,7 @@ describe('transformBody()', () => { ); // verify shape of example object - expect(Object.entries((result!.contents![0].examples![0] as INodeExample).value as unknown[])).toEqual( + expect(Object.entries((result!.contents![0].examples![0] as INodeExample).value)).toEqual( expect.arrayContaining([ [expect.stringMatching(/^_gen_[0-9a-f]{6}$/), 'v1'], [expect.stringMatching(/^_gen_[0-9a-f]{6}$/), 'v2'], @@ -264,7 +264,7 @@ describe('transformBody()', () => { // ensure both share exactly the same keys expect(Object.keys(result!.contents![0].schema!.properties!).sort()).toEqual( - Object.keys((result!.contents![0].examples![0] as INodeExample).value as unknown[]).sort(), + Object.keys((result!.contents![0].examples![0] as INodeExample).value).sort(), ); }); }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 341e8fa4..8ab4f976 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -6,13 +6,13 @@ import { IHttpQueryParam, IMediaTypeContent, } from '@stoplight/types'; -import type { JSONSchema7 } from 'json-schema'; +import { JSONSchema7 } from 'json-schema'; // @ts-ignore import * as jsonSchemaGenerator from 'json-schema-generator'; import { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; -import { convertSchema } from '../../oas/transformers/schema'; +import { translateSchemaObject } from '../../oas/transformers/schema'; import { transformDescriptionDefinition, transformStringValueToSchema } from '../util'; export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { @@ -82,7 +82,7 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): value: parsed, }, ], - schema: convertSchema(jsonSchemaGenerator(parsed)), + schema: translateSchemaObject({}, jsonSchemaGenerator(parsed)), }; } catch (e) { /* noop, move on.. */ @@ -104,7 +104,7 @@ function transformParamsBody( params: PropertyList, mediaType: string, ): IMediaTypeContent { - const paramsList: { name: string; schema: JSONSchema7; value: unknown }[] = params.map(item => { + const paramsList: { name: string; schema: JSONSchema7; value: any }[] = params.map(item => { return { name: item.key || generateId(), schema: { diff --git a/src/postman/transformers/securityScheme.ts b/src/postman/transformers/securityScheme.ts index 816b2cb3..ecc087e4 100644 --- a/src/postman/transformers/securityScheme.ts +++ b/src/postman/transformers/securityScheme.ts @@ -1,6 +1,6 @@ import { HttpParamStyles, HttpSecurityScheme, IHttpHeaderParam, IHttpQueryParam } from '@stoplight/types'; +import { isEqual, omit } from 'lodash'; import { Collection, RequestAuth } from 'postman-collection'; -import isEqual = require('lodash.isequal'); export type PostmanSecurityScheme = StandardSecurityScheme | QuerySecurityScheme | HeaderSecurityScheme; @@ -227,9 +227,7 @@ export function isPostmanSecuritySchemeEqual(pss1: PostmanSecurityScheme, pss2: if (pss1.type !== pss2.type) return false; if (isStandardSecurityScheme(pss1) && isStandardSecurityScheme(pss2)) { - const { key: _key, ...pss1SecurityScheme } = pss1.securityScheme; - const { key: _key2, ...pss2SecurityScheme } = pss2.securityScheme; - return isEqual(pss1SecurityScheme, pss2SecurityScheme); + return isEqual(omit(pss1.securityScheme, 'key'), omit(pss2.securityScheme, 'key')); } return isEqual(pss1, pss2); diff --git a/src/types.ts b/src/types.ts index 5ec95eff..f4552e2a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,29 +1,15 @@ -import type { IHttpOperation, IHttpService } from '@stoplight/types'; +import { DeepPartial, IHttpOperation, IHttpService } from '@stoplight/types'; -export type Fragment = Record; - -export interface ITransformServiceOpts { - document: T; +export interface ITransformServiceOpts { + document: DeepPartial; } export type HttpServiceTransformer = (opts: T) => IHttpService; -export interface ITransformOperationOpts { - document: T; +export interface ITransformOperationOpts { + document: DeepPartial; path: string; method: string; } export type HttpOperationTransformer = (opts: T) => IHttpOperation; - -export type TransformerContext = { - maybeResolveLocalRef(target: unknown): unknown; - document: T; -}; - -export type TranslateFunction = ( - this: TransformerContext, - ...params: P -) => R; - -export type ArrayCallbackParameters = [T, number, T[]]; diff --git a/src/utils.ts b/src/utils.ts index 3d177946..4b0d5ea0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,16 @@ -import { hasRef, isLocalRef, isPlainObject, resolveInlineRef } from '@stoplight/json'; +import { hasRef, isLocalRef, resolveInlineRef } from '@stoplight/json'; +import { Dictionary, Optional } from '@stoplight/types'; +import { isObjectLike, map } from 'lodash'; + +export function mapToKeys(collection: Optional) { + return map(collection, Object.keys); +} + +export const isDictionary = (maybeDictionary: unknown): maybeDictionary is Dictionary => + isObjectLike(maybeDictionary); export const maybeResolveLocalRef = (document: unknown, target: unknown): unknown => { - if (isPlainObject(document) && hasRef(target) && isLocalRef(target.$ref)) { + if (isDictionary(document) && hasRef(target) && isLocalRef(target.$ref)) { try { return resolveInlineRef(document, target.$ref); } catch { @@ -11,11 +20,3 @@ export const maybeResolveLocalRef = (document: unknown, target: unknown): unknow return target; }; - -export { isPlainObject as isDictionary }; - -export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; -export function entries(o: T): [string, T][]; -export function entries(o: T): [string, T][] { - return isPlainObject(o) ? Object.entries(o as T) : []; -} diff --git a/yarn.lock b/yarn.lock index 88d5e7d9..b0e76aba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,7 +1081,7 @@ resolved "https://registry.yarnpkg.com/@stoplight/test-utils/-/test-utils-0.0.1.tgz#b0d38c8a0abebda2dacbc2aa6a4f9bec44878e7b" integrity sha512-Cj3waLFR9bYLG8yvgkjXMxOfiVdewRyrKdH5RQpttVNfKj5UF+mElofPcHn/UfiOuxK3t5rbsdMfS26LK5tdQg== -"@stoplight/types@^12.3.0": +"@stoplight/types@^12.3.0", "@stoplight/types@^12.4.0": version "12.4.0" resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.4.0.tgz#7a90e747d3f531742316b7b2969e6c45efd3e968" integrity sha512-ELf0dOdROaKEE0iw1YF9qbL9QjIVfdyiCoBPpUQmvQej9F7tQR/TsYMvyUb8kz+rd49u3hGP/sirVuaTKI1ZOg== @@ -1089,14 +1089,6 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" -"@stoplight/types@^13.0.0-beta.4": - version "13.0.0-beta.4" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.4.tgz#5ea5e93957b20a5effedcbd101e6e9cde7118e48" - integrity sha512-q9Dbyfi1DcZjTuDwptVLTCbeQgtpfTQAKQkbcrs64ayfvc7dPwO+81vYr0dSJsXzlFQktZv5M+eq0QPtJy/HGg== - dependencies: - "@types/json-schema" "^7.0.4" - utility-types "^3.10.0" - "@tootallnate/once@1": version "1.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" @@ -1200,31 +1192,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash.isequal@^4.5.5": - version "4.5.5" - resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz#4fed1b1b00bef79e305de0352d797e9bb816c8ff" - integrity sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg== - dependencies: - "@types/lodash" "*" - -"@types/lodash.pick@^4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@types/lodash.pick/-/lodash.pick-4.4.6.tgz#ae4e8f109e982786313bb6aac4b1a73aefa6e9be" - integrity sha512-u8bzA16qQ+8dY280z3aK7PoWb3fzX5ATJ0rJB6F+uqchOX2VYF02Aqa+8aYiHiHgPzQiITqCgeimlyKFy4OA6g== - dependencies: - "@types/lodash" "*" - -"@types/lodash.pickby@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz#3dc39c2b38432f7a0c5e5627b0d5c0e3878b4f35" - integrity sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.14.180" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" - integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== +"@types/lodash@4.14.157": + version "4.14.157" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.157.tgz#fdac1c52448861dfde1a2e1515dbc46e54926dc8" + integrity sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ== "@types/node@*", "@types/node@>= 8": version "13.9.1" @@ -1282,6 +1253,11 @@ dependencies: "@types/node" "*" +"@types/urijs@~1.19.9": + version "1.19.17" + resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.17.tgz#232ac9884b6a2aeab5dbe70b79cdb91d5067c325" + integrity sha512-ShIlp+8iNGo/yVVfYFoNRqUiaE9wMCzsSl85qTg2/C5l56BTJokU7QeMgVBQ9xhcyhWQP0zGXPBZPPvEG/sRmQ== + "@types/yargs-parser@*": version "13.1.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" @@ -5382,6 +5358,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -5826,11 +5807,6 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -5861,16 +5837,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.pick@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= - -lodash.pickby@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" - integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" From 17abe835159546a5c9a8183c6bd6a833661eb82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Tue, 29 Mar 2022 19:15:40 +0200 Subject: [PATCH 09/16] refactor(oas): rework all translators (#176) --- package.json | 14 +- src/context.ts | 9 + src/guards.ts | 15 + src/merge.ts | 2 +- .../__snapshots__/operation.test.ts.snap | 52 ++-- .../__snapshots__/service.test.ts.snap | 22 -- src/oas/__tests__/accessors.test.ts | 55 ++-- src/oas/__tests__/service.test.ts | 4 +- src/oas/__tests__/tag.test.ts | 17 -- src/oas/__tests__/tags.test.ts | 48 ++++ src/oas/accessors.ts | 68 +++-- src/oas/guards.ts | 35 ++- src/oas/index.ts | 2 +- src/oas/operation.ts | 45 +-- src/oas/service.ts | 52 ++++ src/oas/tag.ts | 5 - src/oas/tags.ts | 45 +++ src/oas/transformers/index.ts | 1 - .../schema/__tests__/schema.spec.ts | 5 +- src/oas/transformers/schema/index.ts | 42 +-- .../transformers/schema/keywords/format.ts | 4 +- src/oas/transformers/security.ts | 0 src/oas/transformers/translateLogo.ts | 8 +- src/oas/types.ts | 15 +- .../__snapshots__/operation.test.ts.snap | 10 +- src/oas2/__tests__/accessors.test.ts | 31 ++- src/oas2/__tests__/operation.test.ts | 10 +- src/oas2/__tests__/service.test.ts | 2 - src/oas2/accessors.ts | 50 ++-- src/oas2/guards.ts | 26 +- src/oas2/operation.ts | 69 ++--- src/oas2/service.ts | 121 ++++---- .../__snapshots__/request.test.ts.snap | 43 ++- .../__tests__/getExamplesFromSchema.test.ts | 30 -- .../transformers/__tests__/params.test.ts | 43 ++- .../transformers/__tests__/request.test.ts | 32 +-- .../transformers/__tests__/responses.test.ts | 31 +-- .../transformers/__tests__/securities.test.ts | 7 +- .../transformers/__tests__/servers.test.ts | 9 +- src/oas2/transformers/content.ts | 0 .../transformers/getExamplesFromSchema.ts | 12 - src/oas2/transformers/params.ts | 194 ++++++++----- src/oas2/transformers/request.ts | 75 +++-- src/oas2/transformers/responses.ts | 65 ++--- src/oas2/transformers/securities.ts | 198 ++++++------- src/oas2/transformers/servers.ts | 62 +++-- src/oas2/types.ts | 10 + .../__snapshots__/operation.test.ts.snap | 11 +- src/oas3/__tests__/operation.test.ts | 43 +-- src/oas3/__tests__/service.test.ts | 8 - src/oas3/accessors.ts | 27 +- src/oas3/guards.ts | 30 +- src/oas3/operation.ts | 35 ++- src/oas3/service.ts | 106 +++---- .../__snapshots__/content.test.ts.snap | 72 +++-- .../__snapshots__/request.test.ts.snap | 23 +- .../__snapshots__/responses.test.ts.snap | 8 +- .../transformers/__tests__/content.test.ts | 59 +--- .../transformers/__tests__/request.test.ts | 63 +++-- .../transformers/__tests__/responses.test.ts | 8 +- .../transformers/__tests__/securities.test.ts | 8 +- .../transformers/__tests__/servers.test.ts | 107 +++++++- src/oas3/transformers/callbacks.ts | 54 ++-- src/oas3/transformers/content.ts | 259 +++++++++++------- src/oas3/transformers/request.ts | 212 ++++++++------ src/oas3/transformers/responses.ts | 70 ++--- src/oas3/transformers/securities.ts | 41 ++- src/oas3/transformers/servers.ts | 107 ++++++-- src/oas3/types.ts | 10 + .../transformers/__tests__/params.test.ts | 4 +- src/postman/transformers/params.ts | 8 +- src/postman/transformers/securityScheme.ts | 6 +- src/types.ts | 24 +- src/utils.ts | 21 +- yarn.lock | 64 ++++- 75 files changed, 1869 insertions(+), 1314 deletions(-) create mode 100644 src/context.ts create mode 100644 src/guards.ts delete mode 100644 src/oas/__tests__/tag.test.ts create mode 100644 src/oas/__tests__/tags.test.ts create mode 100644 src/oas/service.ts delete mode 100644 src/oas/tag.ts create mode 100644 src/oas/tags.ts create mode 100644 src/oas/transformers/security.ts delete mode 100644 src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts create mode 100644 src/oas2/transformers/content.ts delete mode 100644 src/oas2/transformers/getExamplesFromSchema.ts create mode 100644 src/oas2/types.ts create mode 100644 src/oas3/types.ts diff --git a/package.json b/package.json index 64a29b26..6d059d7e 100644 --- a/package.json +++ b/package.json @@ -53,13 +53,14 @@ }, "dependencies": { "@stoplight/json": "^3.17.2", - "@stoplight/types": "^12.4.0", + "@stoplight/types": "^13.0.0-beta.4", + "@types/json-schema": "7.0.5", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", - "@types/urijs": "~1.19.9", - "json-schema": "^0.4.0", "json-schema-generator": "^2.0.6", - "lodash": "^4.17.15", + "lodash.isequal": "^4.5.0", + "lodash.pick": "^4.4.0", + "lodash.pickby": "^4.6.0", "openapi3-ts": "^2.0.1", "postman-collection": "^4.1.0", "tslib": "^2.3.1", @@ -70,8 +71,9 @@ "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", - "@types/json-schema": "7.0.5", - "@types/lodash": "4.14.157", + "@types/lodash.isequal": "^4.5.5", + "@types/lodash.pick": "^4.4.6", + "@types/lodash.pickby": "^4.6.6", "@types/postman-collection": "^3.5.3", "@typescript-eslint/eslint-plugin": "^5.15.0", "@typescript-eslint/parser": "^5.15.0", diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 00000000..d13668e0 --- /dev/null +++ b/src/context.ts @@ -0,0 +1,9 @@ +import { TransformerContext } from './types'; +import { maybeResolveLocalRef } from './utils'; + +export function createContext>(document: T): TransformerContext { + return { + document, + maybeResolveLocalRef: maybeResolveLocalRef.bind(null, document), + }; +} diff --git a/src/guards.ts b/src/guards.ts new file mode 100644 index 00000000..d57fa038 --- /dev/null +++ b/src/guards.ts @@ -0,0 +1,15 @@ +export function isNonNullable(value: T): value is NonNullable { + return value !== undefined && value !== null; +} + +export function isBoolean(input: unknown): input is boolean { + return typeof input === 'boolean'; +} + +export function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +export function isSerializablePrimitive(value: unknown): value is string | number | boolean | null { + return isBoolean(value) || isString(value) || typeof value === 'number' || value === null; +} diff --git a/src/merge.ts b/src/merge.ts index 35522069..b007adae 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -8,7 +8,7 @@ import { IServer, } from '@stoplight/types'; import type { JSONSchema7 as JSONSchema } from 'json-schema'; -import { isEqual } from 'lodash'; +import isEqual = require('lodash.isequal'); function isExclusivelyAnyOfSchema(schema: JSONSchema): schema is { anyOf: JSONSchema[] } { return !!(schema.anyOf && Object.keys(schema).length === 1); diff --git a/src/oas/__tests__/__snapshots__/operation.test.ts.snap b/src/oas/__tests__/__snapshots__/operation.test.ts.snap index 53a1877f..a4942a89 100644 --- a/src/oas/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/operation.test.ts.snap @@ -37,6 +37,10 @@ Array [ "description": "Pet object that needs to be added to the store", "required": true, }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], }, "responses": Array [ Object { @@ -49,7 +53,6 @@ Array [ "security": Array [ Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -66,9 +69,11 @@ Array [ ], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -85,6 +90,7 @@ Array [ "method": "get", "path": "/pets", "request": Object { + "cookie": Array [], "headers": Array [ Object { "name": "Rate-Limit", @@ -95,6 +101,7 @@ Array [ "style": "simple", }, ], + "path": Array [], "query": Array [ Object { "name": "skip", @@ -125,9 +132,11 @@ Array [ "security": Array [], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -140,6 +149,7 @@ Array [ "method": "options", "path": "/pets", "request": Object { + "cookie": Array [], "headers": Array [ Object { "name": "Rate-Limit", @@ -150,6 +160,7 @@ Array [ "style": "simple", }, ], + "path": Array [], "query": Array [ Object { "name": "skip", @@ -202,9 +213,11 @@ Array [ "security": Array [], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -217,7 +230,12 @@ Array [ "id": "?http-operation-id?", "method": "delete", "path": "/pets", - "request": Object {}, + "request": Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], + }, "responses": Array [ Object { "code": "400", @@ -235,9 +253,11 @@ Array [ "security": Array [], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -274,6 +294,8 @@ Array [ "description": "Pet object that needs to be added to the store", "required": true, }, + "cookie": Array [], + "headers": Array [], "path": Array [ Object { "name": "petId", @@ -285,6 +307,7 @@ Array [ "style": "simple", }, ], + "query": Array [], }, "responses": Array [ Object { @@ -326,7 +349,6 @@ Array [ "security": Array [ Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -343,9 +365,11 @@ Array [ ], "servers": Array [ Object { + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, ], @@ -516,14 +540,12 @@ Array [ ], "servers": Array [ Object { - "description": undefined, + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", - "variables": undefined, }, Object { - "description": undefined, + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", - "variables": undefined, }, ], "summary": "Add a new pet to the store", @@ -553,6 +575,7 @@ Array [ "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, + "style": "simple", }, Object { "examples": Array [], @@ -561,6 +584,7 @@ Array [ "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, + "style": "simple", }, ], }, @@ -586,14 +610,12 @@ Array [ "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", - "variables": undefined, }, Object { - "description": undefined, + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", - "variables": undefined, }, ], "summary": "List pets", @@ -723,11 +745,11 @@ Array [ Object { "examples": Array [], "name": "petId", - "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", }, + "style": "simple", }, ], "query": Array [], @@ -792,14 +814,12 @@ Array [ ], "servers": Array [ Object { - "description": undefined, + "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", - "variables": undefined, }, Object { - "description": undefined, + "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", - "variables": undefined, }, ], "summary": "Update an existing pet", diff --git a/src/oas/__tests__/__snapshots__/service.test.ts.snap b/src/oas/__tests__/__snapshots__/service.test.ts.snap index 792d26c6..2312c802 100644 --- a/src/oas/__tests__/__snapshots__/service.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/service.test.ts.snap @@ -14,7 +14,6 @@ Object { "name": "Swagger Petstore", "securitySchemes": Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -28,7 +27,6 @@ Object { "type": "oauth2", }, Object { - "description": undefined, "in": "header", "key": "api_key", "name": "api_key", @@ -37,12 +35,10 @@ Object { ], "servers": Array [ Object { - "description": undefined, "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "description": undefined, "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -50,10 +46,6 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", - "externalDocs": Object { - "description": "Find out more", - "url": "http://swagger.io", - }, "name": "pet", }, Object { @@ -62,10 +54,6 @@ Object { }, Object { "description": "Operations about user", - "externalDocs": Object { - "description": "Find out more about our store", - "url": "http://swagger.io", - }, "name": "user", }, ], @@ -109,12 +97,10 @@ Object { ], "servers": Array [ Object { - "description": undefined, "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { - "description": undefined, "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -122,10 +108,6 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", - "externalDocs": Object { - "description": "Find out more", - "url": "http://swagger.io", - }, "name": "pet", }, Object { @@ -134,10 +116,6 @@ Object { }, Object { "description": "Operations about user", - "externalDocs": Object { - "description": "Find out more about our store", - "url": "http://swagger.io", - }, "name": "user", }, ], diff --git a/src/oas/__tests__/accessors.test.ts b/src/oas/__tests__/accessors.test.ts index e833c713..29879b23 100644 --- a/src/oas/__tests__/accessors.test.ts +++ b/src/oas/__tests__/accessors.test.ts @@ -1,27 +1,29 @@ -import { getOasTags, getValidOasParameters } from '../accessors'; +import { getValidOasParameters } from '../accessors'; +import { OasVersion } from '../types'; describe('getOasParameters', () => { it('should return empty array', () => { - expect(getValidOasParameters({}, undefined, undefined)).toEqual([]); + expect(getValidOasParameters({}, OasVersion.OAS2, undefined, undefined)).toEqual([]); }); it('should fallback to operation parameters', () => { expect( getValidOasParameters( {}, + OasVersion.OAS2, [ - { name: 'n1', in: 'i1' }, - { name: 'n2', in: 'i2' }, + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, ], undefined, ), ).toEqual([ { - in: 'i1', + in: 'header', name: 'n1', }, { - in: 'i2', + in: 'query', name: 'n2', }, ]); @@ -29,17 +31,17 @@ describe('getOasParameters', () => { it('should fallback to path parameters', () => { expect( - getValidOasParameters({}, undefined, [ - { name: 'n1', in: 'i1' }, - { name: 'n2', in: 'i2' }, + getValidOasParameters({}, OasVersion.OAS2, undefined, [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, ]), ).toEqual([ { - in: 'i1', + in: 'header', name: 'n1', }, { - in: 'i2', + in: 'query', name: 'n2', }, ]); @@ -49,45 +51,30 @@ describe('getOasParameters', () => { expect( getValidOasParameters( {}, + OasVersion.OAS3, [ - { name: 'n1', in: 'n1', type: 'array' }, - { name: 'no2', in: 'io2' }, + { name: 'n1', in: 'query', type: 'array' }, + { name: 'no2', in: 'header' }, ], [ - { name: 'n1', in: 'n1', type: 'string' }, - { name: 'np3', in: 'ip3' }, + { name: 'n1', in: 'query', type: 'string' }, + { name: 'np3', in: 'header' }, ], ), ).toEqual([ { - in: 'n1', + in: 'query', name: 'n1', type: 'array', }, { - in: 'io2', + in: 'header', name: 'no2', }, { - in: 'ip3', + in: 'header', name: 'np3', }, ]); }); }); - -describe('getOasTags', () => { - describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { - it('should return empty array', () => { - expect(getOasTags(tags)).toEqual([]); - }); - }); - - it('should filter out invalid values', () => { - expect(getOasTags([{}, null, 'foo'])).toEqual(['foo']); - }); - - it('should normalize values', () => { - expect(getOasTags([0, 'foo', true])).toEqual(['0', 'foo', 'true']); - }); -}); diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 6a8d8f8a..2a0b0884 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -1,7 +1,7 @@ import { transformOas2Service } from '../../oas2/service'; import { transformOas3Service } from '../../oas3/service'; -import * as oas2KitchenSinkJson from './fixtures//oas2-kitchen-sink.json'; -import * as oas3KitchenSinkJson from './fixtures//oas3-kitchen-sink.json'; +import * as oas2KitchenSinkJson from './fixtures/oas2-kitchen-sink.json'; +import * as oas3KitchenSinkJson from './fixtures/oas3-kitchen-sink.json'; describe('oas service', () => { it('openapi v2', () => { diff --git a/src/oas/__tests__/tag.test.ts b/src/oas/__tests__/tag.test.ts deleted file mode 100644 index 381e5b43..00000000 --- a/src/oas/__tests__/tag.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { translateToTags } from '../tag'; - -describe('translateToTags', () => { - it('should translate array of strings to tags', () => { - expect(translateToTags(['a', 'b', 'c'])).toEqual([ - { - name: 'a', - }, - { - name: 'b', - }, - { - name: 'c', - }, - ]); - }); -}); diff --git a/src/oas/__tests__/tags.test.ts b/src/oas/__tests__/tags.test.ts new file mode 100644 index 00000000..9efcbaa5 --- /dev/null +++ b/src/oas/__tests__/tags.test.ts @@ -0,0 +1,48 @@ +import { createContext } from '../../context'; +import { translateToTags as _translateToTags } from '../tags'; + +const translateToTags = (tags: unknown) => _translateToTags.call(createContext({}), tags); + +describe('translateToTags', () => { + describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { + it('should return empty array', () => { + expect(translateToTags(tags)).toStrictEqual([]); + }); + }); + + it('should filter out invalid values', () => { + expect(translateToTags([{}, null, 'foo'])).toStrictEqual([ + { + name: 'foo', + }, + ]); + }); + + it('should normalize values', () => { + expect(translateToTags([0, 'foo', true])).toStrictEqual([ + { + name: '0', + }, + { + name: 'foo', + }, + { + name: 'true', + }, + ]); + }); + + it('should translate array of strings to tags', () => { + expect(translateToTags(['a', 'b', 'c'])).toStrictEqual([ + { + name: 'a', + }, + { + name: 'b', + }, + { + name: 'c', + }, + ]); + }); +}); diff --git a/src/oas/accessors.ts b/src/oas/accessors.ts index 5ab865d9..50675aad 100644 --- a/src/oas/accessors.ts +++ b/src/oas/accessors.ts @@ -1,34 +1,52 @@ -import { Extensions } from '@stoplight/types'; -import { fromPairs, isObject, map, unionBy } from 'lodash'; +import type { Extensions } from '@stoplight/types'; -import { maybeResolveLocalRef } from '../utils'; - -type ParamTypeBase = { name: string; in: string }; +import { Fragment } from '../types'; +import { entries, maybeResolveLocalRef } from '../utils'; +import { isValidOas2Param, isValidOas3Param, Oas2ParamBase, Oas3ParamBase, ParamBase } from './guards'; +import { OasVersion } from './types'; const ROOT_EXTENSIONS = ['x-internal']; -export function getValidOasParameters( - document: unknown, - operationParameters: ParamType[] | undefined, - pathParameters: ParamType[] | undefined, -) { - const resolvedOperationParams = map(operationParameters, x => maybeResolveLocalRef(document, x) as ParamType); - const resolvedPathParams = map(pathParameters, x => maybeResolveLocalRef(document, x) as ParamType); - - return unionBy(resolvedOperationParams, resolvedPathParams, (parameter?: ParamType) => { - return isObject(parameter) ? `${parameter.name}-${parameter.in}` : 'invalid'; - }) - .filter(isObject) - .filter(isValidOasParameter); -} +function getParameters(document: Fragment, spec: OasVersion.OAS2, params: unknown): Oas2ParamBase[]; +function getParameters(document: Fragment, spec: OasVersion.OAS3, params: unknown): Oas3ParamBase[]; +function getParameters(document: Fragment, spec: OasVersion, params: unknown): ParamBase[] { + if (!Array.isArray(params)) return []; -const isValidOasParameter = (parameter: Partial): parameter is ParamTypeBase => - 'name' in parameter && typeof parameter.name === 'string' && 'in' in parameter && typeof parameter.in === 'string'; + const resolved = params.map(maybeResolveLocalRef.bind(null, document)); + return spec === OasVersion.OAS2 ? resolved.filter(isValidOas2Param) : resolved.filter(isValidOas3Param); +} -export function getOasTags(tags: unknown): string[] { - return Array.isArray(tags) ? tags.filter(tag => typeof tag !== 'object').map(String) : []; +const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; + +export function getValidOasParameters( + document: Fragment, + spec: OasVersion.OAS2, + operationParams: unknown, + pathParams: unknown, +): Oas2ParamBase[]; +export function getValidOasParameters( + document: Fragment, + spec: OasVersion.OAS3, + operationParams: unknown, + pathParams: unknown, +): Oas3ParamBase[]; +export function getValidOasParameters( + document: Fragment, + spec: any, + operationParams: unknown, + pathParams: unknown, +): ParamBase[] { + const uniqueParameters: Record = {}; + + const params = [...getParameters(document, spec, operationParams), ...getParameters(document, spec, pathParams)]; + + for (const param of params) { + uniqueParameters[getIdForParameter(param)] ??= param; + } + + return Object.values(uniqueParameters); } -export function getExtensions(target: Record): Extensions { - return fromPairs(Object.entries(target).filter(([key]) => key.startsWith('x-') && !ROOT_EXTENSIONS.includes(key))); +export function getExtensions(target: unknown): Extensions { + return Object.fromEntries(entries(target).filter(([key]) => key.startsWith('x-') && !ROOT_EXTENSIONS.includes(key))); } diff --git a/src/oas/guards.ts b/src/oas/guards.ts index 226c5769..e0605918 100644 --- a/src/oas/guards.ts +++ b/src/oas/guards.ts @@ -1,11 +1,34 @@ -import { DeepPartial, Dictionary } from '@stoplight/types'; -import { InfoObject } from 'openapi3-ts/src/model/OpenApi'; -import { Info } from 'swagger-schema-official'; - -import { isDictionary } from '../utils'; +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial, Dictionary } from '@stoplight/types'; +import { HttpParamStyles } from '@stoplight/types'; +import { ParameterLocation } from 'openapi3-ts'; +import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; +import type { Info } from 'swagger-schema-official'; +import { BaseParameter } from 'swagger-schema-official'; export function hasXLogo( info: DeepPartial, ): info is DeepPartial & { 'x-logo': Dictionary } { - return isDictionary(info['x-logo']); + return isPlainObject(info['x-logo']); } + +const VALID_OAS3_PARAM_LOCATION: ParameterLocation[] = ['query', 'header', 'path', 'cookie']; +const VALID_OAS2_PARAM_LOCATION: BaseParameter['in'][] = ['query', 'header', 'path', 'body', 'formData']; + +const VALID_PARAM_STYLES: HttpParamStyles[] = Object.values(HttpParamStyles); + +export type Oas3ParamBase = { name: string; in: ParameterLocation }; +export type Oas2ParamBase = { name: string; in: BaseParameter['in'] }; +export type ParamBase = { name: string; in: string }; + +export const isValidOasParam = (param: unknown): param is ParamBase => + isPlainObject(param) && typeof param.name === 'string' && typeof param.in === 'string'; + +export const isValidOas2Param = (param: unknown): param is Oas2ParamBase => + isValidOasParam(param) && VALID_OAS2_PARAM_LOCATION.includes(param.in as BaseParameter['in']); + +export const isValidOas3Param = (param: unknown): param is Oas3ParamBase => + isValidOasParam(param) && VALID_OAS3_PARAM_LOCATION.includes(param.in as ParameterLocation); + +export const isValidParamStyle = (style: unknown): style is HttpParamStyles => + VALID_PARAM_STYLES.includes(style as HttpParamStyles); diff --git a/src/oas/index.ts b/src/oas/index.ts index ca4dfd22..633a1ce4 100644 --- a/src/oas/index.ts +++ b/src/oas/index.ts @@ -1,2 +1,2 @@ export * from './operation'; -export { translateSchemaObject } from './transformers/index'; +export * from './types'; diff --git a/src/oas/operation.ts b/src/oas/operation.ts index f4601bf2..94bb6864 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,33 +1,34 @@ -import { IHttpOperation } from '@stoplight/types'; -import { flatten, get, keys, map } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial, IHttpOperation } from '@stoplight/types'; +import type { OpenAPIObject } from 'openapi3-ts'; +import type { Spec } from 'swagger-schema-official'; -import { HttpOperationTransformer } from '../types'; +import type { HttpOperationTransformer } from '../types'; const DEFAULT_METHODS = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch', 'trace']; export function transformOasOperations( - document: Spec | OpenAPIObject, + document: DeepPartial, transformer: HttpOperationTransformer, methods: string[] | null = DEFAULT_METHODS, ): IHttpOperation[] { - const paths = keys(get(document, 'paths')); + const paths = isPlainObject(document.paths) ? Object.keys(document.paths) : []; - return flatten( - map(paths, path => { - let operations = keys(get(document, ['paths', path])); - if (methods !== null) { - operations = operations.filter(pathKey => methods.includes(pathKey)); - } + return paths.flatMap(path => { + const value = document.paths![path]; + if (!isPlainObject(value)) return []; - return operations.map(method => - transformer({ - document, - path, - method, - }), - ); - }), - ); + let operations = Object.keys(value); + if (methods !== null) { + operations = operations.filter(pathKey => methods.includes(pathKey)); + } + + return operations.map(method => + transformer({ + document, + path, + method, + }), + ); + }); } diff --git a/src/oas/service.ts b/src/oas/service.ts new file mode 100644 index 00000000..c6cb6743 --- /dev/null +++ b/src/oas/service.ts @@ -0,0 +1,52 @@ +import { isPlainObject } from '@stoplight/json'; +import { DeepPartial, IHttpService } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; + +import { isNonNullable, isString } from '../guards'; +import { TranslateFunction } from '../types'; +import { hasXLogo } from './guards'; +import { translateTagDefinition } from './tags'; +import { translateLogo } from './transformers/translateLogo'; + +export const transformOasService: TranslateFunction | DeepPartial, [], IHttpService> = + function () { + const document = this.document; + + const httpService: IHttpService = { + id: '?http-service-id?', + + version: document.info?.version ?? '', + name: document.info?.title ?? 'no-title', + + ...pickBy( + { + description: document.info?.description, + termsOfService: document.info?.termsOfService, + }, + isString, + ), + + ...pickBy( + { + contact: document.info?.contact, + }, + isPlainObject, + ), + }; + + if (isPlainObject(document.info) && hasXLogo(document.info)) { + httpService.logo = translateLogo(document.info); + } + + const tags = Array.isArray(document.tags) + ? document.tags.map(translateTagDefinition, this).filter(isNonNullable) + : []; + + if (tags.length > 0) { + httpService.tags = tags; + } + + return httpService; + }; diff --git a/src/oas/tag.ts b/src/oas/tag.ts deleted file mode 100644 index 1d9fd9c2..00000000 --- a/src/oas/tag.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { INodeTag } from '@stoplight/types'; - -export function translateToTags(tags: string[]): INodeTag[] { - return tags.map(tag => ({ name: tag })); -} diff --git a/src/oas/tags.ts b/src/oas/tags.ts new file mode 100644 index 00000000..7f1ec25d --- /dev/null +++ b/src/oas/tags.ts @@ -0,0 +1,45 @@ +import { isPlainObject } from '@stoplight/json'; +import type { INodeTag, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); + +import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; +import type { ArrayCallbackParameters, Fragment, TranslateFunction } from '../types'; + +export const translateTag: TranslateFunction< + Fragment, + ArrayCallbackParameters, + Optional +> = function (tag) { + if (tag === null || !isSerializablePrimitive(tag)) return; + + return { + name: String(tag), + }; +}; + +export const translateTagDefinition: TranslateFunction< + Fragment, + ArrayCallbackParameters, + Optional +> = function (tag, ...params) { + if (!isPlainObject(tag)) return; + + const translatedTag = translateTag.call(this, tag.name, ...params); + + if (!translatedTag) return; + + return { + ...translatedTag, + + ...pickBy( + { + description: tag.description, + }, + isString, + ), + }; +}; + +export const translateToTags: TranslateFunction = function (tags) { + return Array.isArray(tags) ? tags.map(translateTag, this).filter(isNonNullable) : []; +}; diff --git a/src/oas/transformers/index.ts b/src/oas/transformers/index.ts index d0152846..452614f3 100644 --- a/src/oas/transformers/index.ts +++ b/src/oas/transformers/index.ts @@ -1,2 +1 @@ export { translateSchemaObject } from './schema/index'; -export { translateLogo } from './translateLogo'; diff --git a/src/oas/transformers/schema/__tests__/schema.spec.ts b/src/oas/transformers/schema/__tests__/schema.spec.ts index 02618bcd..bea202ba 100644 --- a/src/oas/transformers/schema/__tests__/schema.spec.ts +++ b/src/oas/transformers/schema/__tests__/schema.spec.ts @@ -1,7 +1,8 @@ +import { createContext } from '../../../../context'; import { translateSchemaObject } from '..'; -import { OASSchemaObject } from '../types'; +import type { OASSchemaObject } from '../types'; -const translate = (schemaObject: OASSchemaObject) => translateSchemaObject({}, schemaObject); +const translate = (schemaObject: OASSchemaObject) => translateSchemaObject.call(createContext({}), schemaObject); describe('translateSchemaObject', () => { it('should translate id', () => { diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index 225424c2..cd12e29c 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -1,11 +1,12 @@ -import { DeepPartial } from '@stoplight/types'; +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial } from '@stoplight/types'; import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; -import { isObject } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import type { OpenAPIObject } from 'openapi3-ts'; +import type { Spec } from 'swagger-schema-official'; +import type { TranslateFunction } from '../../../types'; import keywords from './keywords'; -import { OASSchemaObject } from './types'; +import type { OASSchemaObject } from './types'; const keywordsKeys = Object.keys(keywords); @@ -15,10 +16,13 @@ type InternalOptions = { // Convert from OpenAPI 2.0, OpenAPI 3.0 `SchemaObject` or JSON Schema Draft4/6 to JSON Schema Draft 7 // This converter shouldn't make any differences to Schema objects defined in OpenAPI 3.1, excepts when jsonSchemaDialect is provided. -export function translateSchemaObject( - document: DeepPartial, - schema: OASSchemaObject, -): JSONSchema7 { +export const translateSchemaObject: TranslateFunction< + DeepPartial, + [schema: OASSchemaObject], + JSONSchema7 +> = function (schema) { + const document = this.document; + if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { return { $schema: document.jsonSchemaDialect, @@ -28,7 +32,11 @@ export function translateSchemaObject( }; } - const clonedSchema = convertSchema(schema, { + return convertSchema(schema); +}; + +export function convertSchema(schema: OASSchemaObject) { + const clonedSchema = _convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); @@ -36,7 +44,7 @@ export function translateSchemaObject( return clonedSchema as JSONSchema7; } -function convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONSchema7 { +function _convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONSchema7 { const clonedSchema: OASSchemaObject | JSONSchema7 = { ...schema }; for (const struct of options.structs) { @@ -44,19 +52,19 @@ function convertSchema(schema: OASSchemaObject, options: InternalOptions): JSONS clonedSchema[struct] = clonedSchema[struct].slice(); for (let i = 0; i < clonedSchema[struct].length; i++) { - if (isObject(clonedSchema[struct][i])) { - clonedSchema[struct][i] = convertSchema(clonedSchema[struct][i], options); + if (typeof clonedSchema[struct][i] === 'object' && clonedSchema[struct][i] !== null) { + clonedSchema[struct][i] = _convertSchema(clonedSchema[struct][i], options); } else { clonedSchema[struct].splice(i, 1); i--; } } } else if (clonedSchema[struct] !== null && typeof clonedSchema[struct] === 'object') { - clonedSchema[struct] = convertSchema(clonedSchema[struct], options); + clonedSchema[struct] = _convertSchema(clonedSchema[struct], options); } } - if ('properties' in clonedSchema && isObject(clonedSchema.properties)) { + if ('properties' in clonedSchema && isPlainObject(clonedSchema.properties)) { convertProperties(clonedSchema, options); } @@ -76,8 +84,8 @@ function convertProperties(schema: OASSchemaObject, options: InternalOptions): v for (const key of Object.keys(props)) { const property = props[key]; - if (isObject(property)) { - props[key] = convertSchema(property, options); + if (isPlainObject(property)) { + props[key] = _convertSchema(property, options); } } } diff --git a/src/oas/transformers/schema/keywords/format.ts b/src/oas/transformers/schema/keywords/format.ts index 9f558e9e..3ea30f56 100644 --- a/src/oas/transformers/schema/keywords/format.ts +++ b/src/oas/transformers/schema/keywords/format.ts @@ -1,6 +1,6 @@ -import { JSONSchema7 } from 'json-schema'; +import type { JSONSchema7 } from 'json-schema'; -import { Converter } from '../types'; +import type { Converter } from '../types'; const ranges = { MIN_INT_32: 0 - 2 ** 31, diff --git a/src/oas/transformers/security.ts b/src/oas/transformers/security.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/oas/transformers/translateLogo.ts b/src/oas/transformers/translateLogo.ts index a7ae8301..2e5da953 100644 --- a/src/oas/transformers/translateLogo.ts +++ b/src/oas/transformers/translateLogo.ts @@ -1,7 +1,7 @@ -import { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; -import { pickBy } from 'lodash'; -import { InfoObject } from 'openapi3-ts/src/model/OpenApi'; -import { Info } from 'swagger-schema-official'; +import type { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; +import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; +import type { Info } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); export function translateLogo({ 'x-logo': logo, diff --git a/src/oas/types.ts b/src/oas/types.ts index 16c7bcb0..884fa22d 100644 --- a/src/oas/types.ts +++ b/src/oas/types.ts @@ -1,7 +1,8 @@ -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import { DeepPartial } from '@stoplight/types'; +import type { OpenAPIObject } from 'openapi3-ts'; +import type { Spec } from 'swagger-schema-official'; -import { +import type { HttpOperationTransformer, HttpServiceTransformer, ITransformOperationOpts, @@ -16,15 +17,15 @@ export enum OasVersion { /** * Service */ -export type Oas2TransformServiceOpts = ITransformServiceOpts; -export type Oas3TransformServiceOpts = ITransformServiceOpts; +export type Oas2TransformServiceOpts = ITransformServiceOpts>; +export type Oas3TransformServiceOpts = ITransformServiceOpts>; export type Oas2HttpServiceTransformer = HttpServiceTransformer; export type Oas3HttpServiceTransformer = HttpServiceTransformer; /** * Operation */ -export type Oas2TransformOperationOpts = ITransformOperationOpts; -export type Oas3TransformOperationOpts = ITransformOperationOpts; +export type Oas2TransformOperationOpts = ITransformOperationOpts>; +export type Oas3TransformOperationOpts = ITransformOperationOpts>; export type Oas2HttpOperationTransformer = HttpOperationTransformer; export type Oas3HttpOperationTransformer = HttpOperationTransformer; diff --git a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap index 9215c3f7..53357496 100644 --- a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap @@ -9,7 +9,12 @@ Object { "iid": "oid", "method": "get", "path": "/users/{userId}", - "request": Object {}, + "request": Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], + }, "responses": Array [ Object { "code": "response", @@ -28,7 +33,6 @@ Object { "description": "desc", "headers": Array [ Object { - "description": undefined, "name": "header", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -42,7 +46,6 @@ Object { "security": Array [ Array [ Object { - "description": undefined, "flows": Object { "implicit": Object { "authorizationUrl": "https://petstore.swagger.io/oauth/dialog", @@ -57,7 +60,6 @@ Object { ], Array [ Object { - "description": undefined, "in": "header", "key": "api_key", "name": "api_key_name", diff --git a/src/oas2/__tests__/accessors.test.ts b/src/oas2/__tests__/accessors.test.ts index bd8b6965..08e2b16b 100644 --- a/src/oas2/__tests__/accessors.test.ts +++ b/src/oas2/__tests__/accessors.test.ts @@ -1,7 +1,7 @@ import { Dictionary } from '@stoplight/types'; import { Security } from 'swagger-schema-official'; -import { getConsumes, getProduces, getSecurities } from '../accessors'; +import { getConsumes, getExamplesFromSchema, getProduces, getSecurities } from '../accessors'; const securityDefinitionsFixture: Dictionary = { api_key: { @@ -268,4 +268,33 @@ describe('accessors', () => { expect(getConsumes({ consumes: ['text/plain', null] } as any, {})).toEqual(['text/plain']); }); }); + + describe('getExamplesFromSchema', () => { + it('should ignore invalid data', () => { + // @ts-ignore + expect(getExamplesFromSchema(null)).toEqual({}); + }); + + it('should work with x-examples', () => { + expect( + getExamplesFromSchema({ + 'x-examples': { + 'my-example': {}, + }, + }), + ).toEqual({ + 'my-example': {}, + }); + }); + + it('should work with example', () => { + expect( + getExamplesFromSchema({ + example: 'my-example', + }), + ).toEqual({ + default: 'my-example', + }); + }); + }); }); diff --git a/src/oas2/__tests__/operation.test.ts b/src/oas2/__tests__/operation.test.ts index 25f64672..044016c9 100644 --- a/src/oas2/__tests__/operation.test.ts +++ b/src/oas2/__tests__/operation.test.ts @@ -158,7 +158,12 @@ describe('transformOas2Operation', () => { id: '?http-operation-id?', path: '/users/{userId}', method: 'put', - request: {}, + request: { + cookie: [], + headers: [], + path: [], + query: [], + }, responses: [], security: [], servers: [], @@ -197,6 +202,7 @@ describe('transformOas2Operation', () => { method: 'get', path: '/users/{userId}', request: { + cookie: [], headers: [ { name: 'name', @@ -206,6 +212,8 @@ describe('transformOas2Operation', () => { style: 'simple', }, ], + path: [], + query: [], }, responses: [], security: [], diff --git a/src/oas2/__tests__/service.test.ts b/src/oas2/__tests__/service.test.ts index 34e41e9e..754cd2f5 100644 --- a/src/oas2/__tests__/service.test.ts +++ b/src/oas2/__tests__/service.test.ts @@ -41,12 +41,10 @@ describe('oas2 service', () => { version: '1.0', servers: [ { - description: void 0, name: '', url: 'https://petstore.swagger.io/v2', }, { - description: void 0, name: '', url: 'http://petstore.swagger.io/v2', }, diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index e89d0523..bbcfe522 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,22 +1,20 @@ +import { isPlainObject } from '@stoplight/json'; import { DeepPartial, Dictionary } from '@stoplight/types'; -import { compact, get, isEmpty, isString, keys, map, merge, pickBy } from 'lodash'; -import { negate } from 'lodash/fp'; -import { Operation, Security, Spec } from 'swagger-schema-official'; +import { Operation, Schema, Security, Spec } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); +import { isNonNullable, isString } from '../guards'; import { isSecurityScheme } from './guards'; export type SecurityWithKey = Security & { key: string }; -export function getSecurities( - spec: DeepPartial, - operationSecurity: Dictionary[] | undefined, -): SecurityWithKey[][] { +export function getSecurities(spec: DeepPartial, operationSecurity: unknown): SecurityWithKey[][] { const globalSecurities = getSecurity(spec.security, spec.securityDefinitions || {}); const operationSecurities = getSecurity(operationSecurity, spec.securityDefinitions || {}); const securities = !!operationSecurity ? operationSecurities : globalSecurities; - return securities.filter(negate(isEmpty)); + return securities.filter(a => a.length); } export function getProduces(spec: DeepPartial, operation: DeepPartial) { @@ -27,22 +25,21 @@ export function getConsumes(spec: DeepPartial, operation: DeepPartial, - definitions: DeepPartial, -): SecurityWithKey[][] { - if (!security || !definitions) { +function getSecurity(security: unknown, definitions: DeepPartial): SecurityWithKey[][] { + if (!Array.isArray(security) || !definitions) { return []; } - return map(security, sec => { - return compact( - keys(sec).map(key => { + return security.map(sec => { + if (!isPlainObject(sec)) return []; + return Object.keys(sec) + .map(key => { const def = definitions[key]; if (isSecurityScheme(def)) { - const defCopy = merge<{ key: string }, Security>({ key }, def); - const scopes = sec[key] || []; + const defCopy = { ...def, key }; + const secKey = sec[key]; + const scopes = Array.isArray(secKey) ? secKey : []; // Filter definition scopes by operation scopes if (defCopy.type === 'oauth2' && scopes.length) { @@ -53,8 +50,8 @@ function getSecurity( } return null; - }), - ); + }) + .filter(isNonNullable); }); } @@ -63,10 +60,19 @@ function getProducesOrConsumes( spec: DeepPartial, operation: DeepPartial, ): string[] { - const mimeTypes = get(operation, which, get(spec, which, [])); + const mimeTypes = operation?.[which] || spec?.[which] || []; if (!Array.isArray(mimeTypes)) { return []; } - return compact(mimeTypes).filter(v => v && isString(v)); + return mimeTypes.flat().filter(isString); +} + +export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Record { + if (!isPlainObject(data)) return {}; + + return { + ...('x-examples' in data && isPlainObject(data['x-examples']) && { ...data['x-examples'] }), + ...('example' in data && { default: data.example }), + }; } diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index 7708867e..bf5d124f 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -1,22 +1,18 @@ -import { isObject } from 'lodash'; +import { isPlainObject } from '@stoplight/json'; import type { Response, Security, Tag } from 'swagger-schema-official'; -import { isDictionary } from '../utils'; +import { isValidOas2Param, Oas2ParamBase } from '../oas/guards'; export function isSecurityScheme(maybeSecurityScheme: unknown): maybeSecurityScheme is Security { - return isDictionary(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; + return isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; } export const isTagObject = (maybeTagObject: unknown): maybeTagObject is Tag => { - if (isObject(maybeTagObject) && 'name' in maybeTagObject) { - return typeof (maybeTagObject as Tag).name === 'string'; - } - - return false; + return isPlainObject(maybeTagObject) && typeof maybeTagObject.name === 'string'; }; export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObject is Response => - isObject(maybeResponseObject) && + isPlainObject(maybeResponseObject) && ('description' in maybeResponseObject || 'schema' in maybeResponseObject || 'headers' in maybeResponseObject || @@ -25,3 +21,15 @@ export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObj export function isValidScheme(scheme: unknown): scheme is 'http' | 'https' | 'ws' | 'wss' { return typeof scheme === 'string' && ['http', 'https', 'ws', 'wss'].includes(scheme); } + +export function isQueryParam(param: unknown): param is Oas2ParamBase & { in: 'query' } { + return isValidOas2Param(param) && param.in === 'query'; +} + +export function isPathParam(param: unknown): param is Oas2ParamBase & { in: 'path' } { + return isValidOas2Param(param) && param.in === 'path'; +} + +export function isHeaderParam(param: unknown): param is Oas2ParamBase & { in: 'header' } { + return isValidOas2Param(param) && param.in === 'header'; +} diff --git a/src/oas2/operation.ts b/src/oas2/operation.ts index bece36aa..44365397 100644 --- a/src/oas2/operation.ts +++ b/src/oas2/operation.ts @@ -1,13 +1,15 @@ -import { IHttpOperation } from '@stoplight/types'; -import { get, isNil, omitBy } from 'lodash'; -import { Operation, Parameter, Path, Response, Spec } from 'swagger-schema-official'; +import { isPlainObject } from '@stoplight/json'; +import type { IHttpOperation } from '@stoplight/types'; +import type { Spec } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); -import { getExtensions, getOasTags, getValidOasParameters } from '../oas/accessors'; +import { createContext } from '../context'; +import { isBoolean, isString } from '../guards'; +import { getExtensions } from '../oas/accessors'; import { transformOasOperations } from '../oas/operation'; -import { translateToTags } from '../oas/tag'; +import { translateToTags } from '../oas/tags'; import { Oas2HttpOperationTransformer } from '../oas/types'; import { maybeResolveLocalRef } from '../utils'; -import { getConsumes, getProduces } from './accessors'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; @@ -18,42 +20,45 @@ export function transformOas2Operations(document: Spec): IHttpOperation[] { } export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, get(document, ['paths', path])) as Path; - if (!pathObj) { + const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]); + if (!isPlainObject(pathObj)) { throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); } - const operation = maybeResolveLocalRef(document, get(document, ['paths', path, method])) as Operation & { - [extension: string]: unknown; - }; - if (!operation) { + const operation = maybeResolveLocalRef(document, pathObj[method]); + if (!isPlainObject(operation)) { throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); } - const produces = getProduces(document, operation); - const consumes = getConsumes(document, operation); + const ctx = createContext(document); - const httpOperation: IHttpOperation = { - // TODO(SL-248): what shall we do with id? + return { id: '?http-operation-id?', - iid: operation.operationId, - description: operation.description, - deprecated: operation.deprecated, - internal: operation['x-internal'] as boolean, method, path, - summary: operation.summary, - responses: translateToResponses(document, operation.responses as { [name: string]: Response }, produces), - servers: translateToServers(document, operation), - request: translateToRequest( - document, - getValidOasParameters(document, operation.parameters as Parameter[], pathObj.parameters as Parameter[]), - consumes, - ), - tags: translateToTags(getOasTags(operation.tags)), - security: translateToSecurities(document, operation.security), + + responses: translateToResponses.call(ctx, operation), + servers: translateToServers.call(ctx, operation), + request: translateToRequest.call(ctx, pathObj, operation), + tags: translateToTags.call(ctx, operation.tags), + security: translateToSecurities.call(ctx, operation.security), extensions: getExtensions(operation), - }; - return omitBy(httpOperation, isNil) as IHttpOperation; + ...pickBy( + { + deprecated: operation.deprecated, + internal: operation['x-internal'], + }, + isBoolean, + ), + + ...pickBy( + { + iid: operation.operationId, + description: operation.description, + summary: operation.summary, + }, + isString, + ), + }; }; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index d8eb9f6c..a67f48d6 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,27 +1,17 @@ -import { HttpSecurityScheme, IHttpService, IServer } from '@stoplight/types'; -import { compact, filter, flatMap, isString, keys, pickBy } from 'lodash'; +import { isPlainObject } from '@stoplight/json'; +import pickBy = require('lodash.pickby'); -import { hasXLogo } from '../oas/guards'; -import { translateLogo } from '../oas/transformers/translateLogo'; +import { createContext } from '../context'; +import { isNonNullable, isString } from '../guards'; +import { transformOasService } from '../oas/service'; import { Oas2HttpServiceTransformer } from '../oas/types'; -import { isDictionary } from '../utils'; -import { isTagObject } from './guards'; +import { entries } from '../utils'; import { translateToSingleSecurity } from './transformers/securities'; +import { translateToServer } from './transformers/servers'; export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) => { - const httpService: IHttpService = { - id: '?http-service-id?', - version: document.info?.version ?? '', - name: document.info?.title ?? 'no-title', - }; - - if (document.info?.description) { - httpService.description = document.info.description; - } - - if (document.info?.contact) { - httpService.contact = document.info.contact; - } + const ctx = createContext(document); + const httpService = transformOasService.call(ctx); if (document.info?.license) { httpService.license = { @@ -30,75 +20,60 @@ export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) = }; } - if (document.info?.termsOfService) { - httpService.termsOfService = document.info.termsOfService; - } + const schemes = Array.isArray(document.schemes) ? document.schemes.filter(isString) : []; - if (isDictionary(document.info) && hasXLogo(document.info)) { - httpService.logo = translateLogo(document.info); - } + const servers = schemes.map(translateToServer, ctx).filter(isNonNullable); - const schemes = filter(document.schemes, scheme => scheme && isString(scheme)); - const servers = schemes.map(scheme => ({ - name: document.info?.title ?? '', - description: undefined, - url: scheme + '://' + (document.host || '') + (document.basePath || ''), - })); if (servers.length) { httpService.servers = servers; } - const securitySchemes = compact( - keys(document.securityDefinitions).map(key => { - const definition = document?.securityDefinitions?.[key]; - if (!definition) return undefined; + const securitySchemes = entries(document.securityDefinitions) + .map(([key, definition]) => { + return isPlainObject(definition) ? translateToSingleSecurity.call(ctx, { ...definition, key }) : null; + }) + .filter(isNonNullable); - return translateToSingleSecurity(definition, key); - }), - ); if (securitySchemes.length) { httpService.securitySchemes = securitySchemes; } - const security = compact( - flatMap(document.security, sec => { - if (!sec) return null; - - return keys(sec).map(key => { - const ss = securitySchemes.find(securityScheme => securityScheme.key === key); - if (ss && ss.type === 'oauth2') { - const flows = {}; - for (const flowKey in ss.flows) { - const flow = ss.flows[flowKey]; - flows[flowKey] = { - ...flow, - scopes: pickBy(flow.scopes, (_val: string, scopeKey: string) => { - const secKey = sec[key]; - if (secKey) return secKey.includes(scopeKey); - return undefined; - }), - }; - } - - return { - ...ss, - flows, - }; - } - - return ss; - }); - }), - ); + const security = Array.isArray(document.security) + ? document.security + .flatMap(sec => { + if (!isPlainObject(sec)) return null; + + return Object.keys(sec).map(key => { + const ss = securitySchemes.find(securityScheme => securityScheme.key === key); + if (ss && ss.type === 'oauth2') { + const flows = {}; + for (const flowKey in ss.flows) { + const flow = ss.flows[flowKey]; + flows[flowKey] = { + ...flow, + scopes: pickBy(flow.scopes, (_val: string, scopeKey: string) => { + const secKey = sec[key]; + if (secKey) return secKey.includes(scopeKey); + return undefined; + }), + }; + } + + return { + ...ss, + flows, + }; + } + + return ss; + }); + }) + .filter(isNonNullable) + : []; if (security.length) { httpService.security = security; } - const tags = filter(document.tags, isTagObject); - if (tags.length) { - httpService.tags = tags; - } - return httpService; }; diff --git a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap index 3511832c..c496d16a 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -6,6 +6,10 @@ Object { "in": "body", "name": "param", }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], } `; @@ -16,22 +20,31 @@ Object { "name": "param", "type": "number", }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], } `; exports[`request given single header param should translate to request with header 1`] = ` Object { + "cookie": Array [], "headers": Array [ Object { "in": "header", "name": "param", }, ], + "path": Array [], + "query": Array [], } `; exports[`request given single path param should translate to request with path 1`] = ` Object { + "cookie": Array [], + "headers": Array [], "path": Array [ Object { "in": "path", @@ -39,11 +52,15 @@ Object { "required": true, }, ], + "query": Array [], } `; exports[`request given single query param should translate to request with query 1`] = ` Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], "query": Array [ Object { "in": "query", @@ -55,47 +72,43 @@ Object { exports[`request given two header params should translate 1`] = ` Object { + "cookie": Array [], "headers": Array [ Object { "in": "header", "name": "param", }, - Object { - "in": "header", - "name": "param", - }, ], + "path": Array [], + "query": Array [], } `; exports[`request given two path params should translate 1`] = ` Object { + "cookie": Array [], + "headers": Array [], "path": Array [ Object { "in": "path", "name": "param", "required": true, }, - Object { - "in": "path", - "name": "param", - "required": true, - }, ], + "query": Array [], } `; exports[`request given two query params should translate 1`] = ` Object { + "cookie": Array [], + "headers": Array [], + "path": Array [], "query": Array [ Object { "in": "query", "name": "param", }, - Object { - "in": "query", - "name": "param", - }, ], } `; @@ -107,5 +120,9 @@ Object { "name": "param", "type": "number", }, + "cookie": Array [], + "headers": Array [], + "path": Array [], + "query": Array [], } `; diff --git a/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts b/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts deleted file mode 100644 index ceee05df..00000000 --- a/src/oas2/transformers/__tests__/getExamplesFromSchema.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getExamplesFromSchema } from '../getExamplesFromSchema'; - -describe('getExamplesFromSchema', () => { - it('should ignore invalid data', () => { - // @ts-ignore - expect(getExamplesFromSchema(null)).toEqual({}); - }); - - it('should work with x-examples', () => { - expect( - getExamplesFromSchema({ - 'x-examples': { - 'my-example': {}, - }, - }), - ).toEqual({ - 'my-example': {}, - }); - }); - - it('should work with example', () => { - expect( - getExamplesFromSchema({ - example: 'my-example', - }), - ).toEqual({ - default: 'my-example', - }); - }); -}); diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index a7e70c25..2b8158a8 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -1,15 +1,42 @@ -import { HttpParamStyles } from '@stoplight/types'; -import { FormDataParameter, QueryParameter } from 'swagger-schema-official'; +import { DeepPartial, HttpParamStyles } from '@stoplight/types'; +import { FormDataParameter, QueryParameter, Spec } from 'swagger-schema-official'; +import { createContext } from '../../../context'; import { - translateFromFormDataParameters, - translateToBodyParameter, - translateToHeaderParam, - translateToHeaderParams, - translateToPathParameter, - translateToQueryParameter, + translateFromFormDataParameters as _translateFromFormDataParameters, + translateToBodyParameter as _translateToBodyParameter, + translateToHeaderParam as _translateToHeaderParam, + translateToHeaderParams as _translateToHeaderParams, + translateToPathParameter as _translateToPathParameter, + translateToQueryParameter as _translateToQueryParameter, } from '../params'; +const translateFromFormDataParameters = ( + document: DeepPartial, + ...params: Parameters +) => _translateFromFormDataParameters.call(createContext(document), ...params); + +const translateToBodyParameter = ( + document: DeepPartial, + ...params: Parameters +) => _translateToBodyParameter.call(createContext(document), ...params); + +const translateToHeaderParam = (document: DeepPartial, ...params: Parameters) => + _translateToHeaderParam.call(createContext(document), ...params); + +const translateToHeaderParams = (document: DeepPartial, ...params: Parameters) => + _translateToHeaderParams.call(createContext(document), ...params); + +const translateToPathParameter = ( + document: DeepPartial, + ...params: Parameters +) => _translateToPathParameter.call(createContext(document), ...params); + +const translateToQueryParameter = ( + document: DeepPartial, + ...params: Parameters +) => _translateToQueryParameter.call(createContext(document), ...params); + describe('params.translator', () => { let consumes = ['*']; diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index da7f43c5..286e6788 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -6,6 +6,7 @@ import { QueryParameter, } from 'swagger-schema-official'; +import { createContext } from '../../../context'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -13,13 +14,16 @@ import { translateToPathParameter, translateToQueryParameter, } from '../params'; -import { translateToRequest } from '../request'; +import { translateToRequest as _translateToRequest } from '../request'; jest.mock('../params'); -jest.mock('../../guards'); + +const translateToRequest = (path: Record, parameters: any[]) => { + const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }); + return _translateToRequest.call(ctx, path, { parameters }); +}; describe('request', () => { - const consumes = ['*']; const fakeParameter: FormDataParameter = { name: 'name', type: 'string', @@ -43,45 +47,41 @@ describe('request', () => { jest.resetAllMocks(); }); - it('given empty params collection should return empty object', () => { - expect(translateToRequest({}, [], consumes)).toEqual({}); - }); - it('given single body param should translate to request with body', () => { - expect(translateToRequest({}, [fakeBodyParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeBodyParameter])).toMatchSnapshot(); }); it('given single form param should translate to request with form', () => { - expect(translateToRequest({}, [fakeFormParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeFormParameter])).toMatchSnapshot(); }); it('given single path param should translate to request with path', () => { - expect(translateToRequest({}, [fakePathParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakePathParameter])).toMatchSnapshot(); }); it('given single query param should translate to request with query', () => { - expect(translateToRequest({}, [fakeQueryParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeQueryParameter])).toMatchSnapshot(); }); it('given single header param should translate to request with header', () => { - expect(translateToRequest({}, [fakeHeaderParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeHeaderParameter])).toMatchSnapshot(); }); it('given two query params should translate', () => { - expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter])).toMatchSnapshot(); }); it('given two header params should translate', () => { - expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter])).toMatchSnapshot(); }); it('given two path params should translate', () => { - expect(translateToRequest({}, [fakePathParameter, fakePathParameter], consumes)).toMatchSnapshot(); + expect(translateToRequest({}, [fakePathParameter, fakePathParameter])).toMatchSnapshot(); }); it('should translate mixed request', () => { expect( - translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter], consumes), + translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter]), ).toMatchSnapshot(); }); }); diff --git a/src/oas2/transformers/__tests__/responses.test.ts b/src/oas2/transformers/__tests__/responses.test.ts index 8217087e..11881d59 100644 --- a/src/oas2/transformers/__tests__/responses.test.ts +++ b/src/oas2/transformers/__tests__/responses.test.ts @@ -1,11 +1,15 @@ -import { HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; -import { Schema } from 'swagger-schema-official'; +import { DeepPartial, HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; +import { Operation, Schema, Spec } from 'swagger-schema-official'; +import { createContext } from '../../../context'; import { translateToHeaderParams } from '../params'; -import { translateToResponses } from '../responses'; +import { translateToResponses as _translateToResponses } from '../responses'; jest.mock('../params'); +const translateToResponses = (document: DeepPartial, responses: DeepPartial) => + _translateToResponses.call(createContext(document), { responses }); + describe('responses', () => { const fakeHeaderParams: IHttpHeaderParam[] = [{ name: 'fake-header', style: HttpParamStyles.Simple }]; const produces = ['application/json', 'application/xml']; @@ -16,7 +20,7 @@ describe('responses', () => { it('should translate to multiple responses', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -35,7 +39,6 @@ describe('responses', () => { schema: {}, }, }, - produces, ); expect(responses).toMatchSnapshot(); @@ -44,7 +47,7 @@ describe('responses', () => { it('should translate to response w/o headers', () => { expect( translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -54,7 +57,6 @@ describe('responses', () => { schema: {}, }, }, - produces, ), ).toMatchSnapshot(); }); @@ -62,14 +64,13 @@ describe('responses', () => { it('should translate to response w/o examples', () => { expect( translateToResponses( - {}, + { produces }, { r1: { description: 'd1', schema: {}, }, }, - produces, ), ).toMatchSnapshot(); }); @@ -77,7 +78,7 @@ describe('responses', () => { describe('should keep foreign examples', () => { it('aggregating them to the first example', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -89,7 +90,6 @@ describe('responses', () => { schema: {}, }, }, - produces, ); expect(responses[0].contents).toBeDefined(); @@ -104,7 +104,7 @@ describe('responses', () => { describe('given a response with a schema with an example', () => { it('should translate to response with examples', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -116,7 +116,6 @@ describe('responses', () => { }, }, }, - produces, ); expect(responses[0].contents![0]).toHaveProperty('examples', [{ key: 'default', value: { name: 'value' } }]); }); @@ -125,7 +124,7 @@ describe('responses', () => { describe('given multiple schema example properties', () => { it('should translate all examples', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -142,7 +141,6 @@ describe('responses', () => { } as Schema, }, }, - produces, ); expect(responses[0].contents![0]).toHaveProperty('examples', [ { key: 'application/json', value: { name: 'examples value' } }, @@ -154,7 +152,7 @@ describe('responses', () => { describe('given response with examples in root and schema objects', () => { it('root examples should take precedence over schema examples', () => { const responses = translateToResponses( - {}, + { produces }, { r1: { description: 'd1', @@ -170,7 +168,6 @@ describe('responses', () => { }, }, }, - produces, ); expect(responses[0].contents![0].examples).toHaveLength(2); expect(responses[0].contents![0].examples).toEqual([ diff --git a/src/oas2/transformers/__tests__/securities.test.ts b/src/oas2/transformers/__tests__/securities.test.ts index 9bd8d382..9c60c069 100644 --- a/src/oas2/transformers/__tests__/securities.test.ts +++ b/src/oas2/transformers/__tests__/securities.test.ts @@ -1,6 +1,11 @@ +import { DeepPartial } from '@stoplight/types'; import { Spec } from 'swagger-schema-official'; -import { translateToSecurities } from '../securities'; +import { createContext } from '../../../context'; +import { translateToSecurities as _translateToSecurities } from '../securities'; + +const translateToSecurities = (document: DeepPartial, ...params: Parameters) => + _translateToSecurities.call(createContext(document), ...params); describe('securities', () => { describe('translateToSecurities', () => { diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 85d52cee..11cd45d6 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,7 +1,14 @@ -import { translateToServers } from '../servers'; +import type { DeepPartial } from '@stoplight/types'; +import type { Spec } from 'swagger-schema-official'; + +import { createContext } from '../../../context'; +import { translateToServers as _translateToServers } from '../servers'; type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; +const translateToServers = (document: DeepPartial, ...params: Parameters) => + _translateToServers.call(createContext(document), ...params); + describe('translateToServers', () => { afterAll(() => { delete (global as GlobalWithLocation).location; diff --git a/src/oas2/transformers/content.ts b/src/oas2/transformers/content.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/oas2/transformers/getExamplesFromSchema.ts b/src/oas2/transformers/getExamplesFromSchema.ts deleted file mode 100644 index b5018f05..00000000 --- a/src/oas2/transformers/getExamplesFromSchema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Dictionary } from '@stoplight/types'; -import { isObject } from 'lodash'; -import { Schema } from 'swagger-schema-official'; - -export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Dictionary { - if (!isObject(data)) return {}; - - return { - ...('x-examples' in data && isObject(data['x-examples']) && { ...data['x-examples'] }), - ...('example' in data && { default: data.example }), - }; -} diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index 7527594f..9bbcd94d 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -1,3 +1,4 @@ +import { isPlainObject } from '@stoplight/json'; import { DeepPartial, HttpParamStyles, @@ -6,26 +7,31 @@ import { IHttpOperationRequestBody, IHttpPathParam, IHttpQueryParam, + Optional, } from '@stoplight/types'; -import { JSONSchema7 } from 'json-schema'; -import { get, map, pick, pickBy, set } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; -import { +import type { JSONSchema7 } from 'json-schema'; +import type { BodyParameter, FormDataParameter, Header, HeaderParameter, PathParameter, QueryParameter, - Spec, } from 'swagger-schema-official'; +import pickBy = require('lodash.pickby'); +import pick = require('lodash.pick'); +import { isBoolean, isNonNullable, isString } from '../../guards'; +import { Oas2ParamBase } from '../../oas/guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary } from '../../utils'; -import { getExamplesFromSchema } from './getExamplesFromSchema'; +import { ArrayCallbackParameters } from '../../types'; +import { entries } from '../../utils'; +import { getExamplesFromSchema } from '../accessors'; +import { isHeaderParam } from '../guards'; +import { Oas2TranslateFunction } from '../types'; function chooseQueryParameterStyle( - parameter: QueryParameter, + parameter: DeepPartial, ): | HttpParamStyles.PipeDelimited | HttpParamStyles.SpaceDelimited @@ -55,41 +61,46 @@ function chooseQueryParameterStyle( } } -export function translateToHeaderParam(document: DeepPartial, parameter: HeaderParameter): IHttpHeaderParam { - return pickBy({ - ...buildSchemaForParameter(document, parameter), - name: parameter.name, +export const translateToHeaderParam: Oas2TranslateFunction< + [param: DeepPartial & Oas2ParamBase & { in: 'header' }], + IHttpHeaderParam +> = function (param) { + return { style: HttpParamStyles.Simple, - required: parameter.required, - }) as unknown as IHttpHeaderParam; -} + name: param.name, + ...buildSchemaForParameter.call(this, param), -export function translateToHeaderParams( - document: DeepPartial, - headers: { [headerName: string]: Header }, -): IHttpHeaderParam[] { - return map(headers, (header, name) => { - const { schema, description } = buildSchemaForParameter(document, Object.assign({ name }, header)); - - const param: IHttpHeaderParam = { - name, - style: HttpParamStyles.Simple, - schema, - description, - }; - - return param; - }); -} + ...pickBy( + { + required: param.required, + }, + isBoolean, + ), + }; +}; + +const translateToHeaderParamsFromPair: Oas2TranslateFunction< + ArrayCallbackParameters<[name: string, value: unknown]>, + Optional +> = function ([name, value]) { + if (!isPlainObject(value)) return; + const param = { name, in: 'header', ...value }; + if (!isHeaderParam(param)) return; + return translateToHeaderParam.call(this, param); +}; -export function translateToBodyParameter( - document: DeepPartial, - body: BodyParameter, - consumes: string[], -): IHttpOperationRequestBody { - const examples = map( - get(body, 'x-examples') || (body.schema ? getExamplesFromSchema(body.schema) : void 0), - (value, key) => ({ key, value }), +export const translateToHeaderParams: Oas2TranslateFunction<[headers: unknown], IHttpHeaderParam[]> = function ( + headers, +) { + return entries(headers).map(translateToHeaderParamsFromPair, this).filter(isNonNullable); +}; + +export const translateToBodyParameter: Oas2TranslateFunction< + [body: BodyParameter, consumes: string[]], + IHttpOperationRequestBody +> = function (body, consumes) { + const examples = entries(body['x-examples'] || (body.schema ? getExamplesFromSchema(body.schema) : void 0)).map( + ([key, value]) => ({ key, value }), ); return pickBy({ @@ -98,18 +109,17 @@ export function translateToBodyParameter( contents: consumes.map(mediaType => { return { mediaType, - schema: isDictionary(body.schema) ? translateSchemaObject(document, body.schema) : void 0, + schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, examples, }; }), }); -} +}; -export function translateFromFormDataParameters( - document: DeepPartial, - parameters: FormDataParameter[], - consumes: string[], -): IHttpOperationRequestBody { +export const translateFromFormDataParameters: Oas2TranslateFunction< + [parameters: FormDataParameter[], consumes: string[]], + IHttpOperationRequestBody +> = function (parameters, consumes) { const finalBody: IHttpOperationRequestBody = { contents: consumes.map(mediaType => ({ mediaType, @@ -121,7 +131,7 @@ export function translateFromFormDataParameters( }; return parameters.reduce((body, parameter) => { - const { schema, description } = buildSchemaForParameter(document, parameter); + const { schema, description } = buildSchemaForParameter.call(this, parameter); (body.contents || []).forEach(content => { delete schema.$schema; @@ -129,11 +139,12 @@ export function translateFromFormDataParameters( schema.description = description; } - set(content, `schema.properties.${parameter.name}`, schema); + content.schema ||= {}; + content.schema.properties ||= {}; + content.schema.properties[parameter.name] = schema; if (parameter.required) { - const requiredIndex = get(content, 'schema.required.length', 0); - set(content, `schema.required.${requiredIndex}`, parameter.name); + (content.schema.required ||= []).push(parameter.name); } if (parameter.collectionFormat) { @@ -146,7 +157,7 @@ export function translateFromFormDataParameters( }); return body; }, finalBody); -} +}; function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { switch (parameter.collectionFormat) { @@ -178,29 +189,48 @@ function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { return null; } -export function translateToQueryParameter(document: DeepPartial, query: QueryParameter): IHttpQueryParam { - return pickBy({ - ...buildSchemaForParameter(document, query), - allowEmptyValue: query.allowEmptyValue, - name: query.name, +export const translateToQueryParameter: Oas2TranslateFunction< + [query: DeepPartial & Oas2ParamBase], + IHttpQueryParam +> = function (query) { + return { style: chooseQueryParameterStyle(query), - required: query.required, - }) as unknown as IHttpQueryParam; -} + name: query.name, + ...buildSchemaForParameter.call(this, query), -export function translateToPathParameter(document: DeepPartial, parameter: PathParameter): IHttpPathParam { - return pickBy({ - ...buildSchemaForParameter(document, parameter), - name: parameter.name, + ...pickBy( + { + required: query.required, + allowEmptyValue: query.allowEmptyValue, + }, + isBoolean, + ), + }; +}; + +export const translateToPathParameter: Oas2TranslateFunction< + [param: DeepPartial & Oas2ParamBase], + IHttpPathParam +> = function (param) { + return { + name: param.name, style: HttpParamStyles.Simple, - required: parameter.required, - }) as unknown as IHttpPathParam; -} -function buildSchemaForParameter( - document: DeepPartial, - param: QueryParameter | PathParameter | HeaderParameter | FormDataParameter | Header, -): { schema: JSONSchema7; description?: string } { + ...buildSchemaForParameter.call(this, param), + + ...pickBy( + { + required: param.required, + }, + isBoolean, + ), + }; +}; + +const buildSchemaForParameter: Oas2TranslateFunction< + [param: DeepPartial], + { schema: JSONSchema7; description?: string; deprecated?: boolean } +> = function (param) { const schema = pick( param, 'type', @@ -220,15 +250,27 @@ function buildSchemaForParameter( 'pattern', 'uniqueItems', 'multipleOf', - ); + ) as Record; if ('allowEmptyValue' in param && param.allowEmptyValue === false) { schema.minLength = 1; } return { - schema: translateSchemaObject(document, schema), - description: param.description, - ...('x-deprecated' in param && { deprecated: param['x-deprecated'] }), + schema: translateSchemaObject.call(this, schema), + + ...pickBy( + { + deprecated: param['x-deprecated'], + }, + isBoolean, + ), + + ...pickBy( + { + description: param.description, + }, + isString, + ), }; -} +}; diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index e7763ce3..cdf894e3 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,6 +1,13 @@ -import { DeepPartial, IHttpOperationRequest } from '@stoplight/types'; -import { BodyParameter, FormDataParameter, Parameter, Spec } from 'swagger-schema-official'; +import type { IHttpOperationRequest } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import type { BodyParameter, FormDataParameter } from 'swagger-schema-official'; +import { isNonNullable } from '../../guards'; +import { OasVersion } from '../../oas'; +import { getValidOasParameters } from '../../oas/accessors'; +import { getConsumes } from '../accessors'; +import { isHeaderParam, isPathParam, isQueryParam } from '../guards'; +import { Oas2TranslateFunction } from '../types'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -9,38 +16,50 @@ import { translateToQueryParameter, } from './params'; -export function translateToRequest( - document: DeepPartial, - parameters: Parameter[], - consumes: string[], -): IHttpOperationRequest { - const bodyParameters = parameters.filter((p): p is BodyParameter => p.in === 'body'); +export const translateToRequest: Oas2TranslateFunction< + [path: Record, operation: Record], + IHttpOperationRequest +> = function (path, operation) { + const consumes = getConsumes(this.document, operation); + const parameters = getValidOasParameters(this.document, OasVersion.OAS2, operation.parameters, path.parameters); + + const bodyParameter = parameters.find((p): p is BodyParameter => p.in === 'body'); const formDataParameters = parameters.filter((p): p is FormDataParameter => p.in === 'formData'); - const request: IHttpOperationRequest = {}; + const params: Omit, 'body'> = { + headers: [], + query: [], + cookie: [], + path: [], + }; + + let body; // if 'body' and 'form data' defined prefer 'body' - if (!!bodyParameters.length) { + if (!!bodyParameter) { // There can be only one body parameter (taking first one) - request.body = translateToBodyParameter(document, bodyParameters[0], consumes); + body = translateToBodyParameter.call(this, bodyParameter, consumes); } else if (!!formDataParameters.length) { - request.body = translateFromFormDataParameters(document, formDataParameters, consumes); + body = translateFromFormDataParameters.call(this, formDataParameters, consumes); } - return parameters.reduce(createReduceRemainingParameters(document), request); -} - -function createReduceRemainingParameters(document: DeepPartial) { - return function (request: IHttpOperationRequest, parameter: Parameter) { - if (parameter.in === 'query') { - const queryParameter = translateToQueryParameter(document, parameter); - request.query = (request.query || []).concat(queryParameter); - } else if (parameter.in === 'path') { - const pathParameter = translateToPathParameter(document, parameter); - request.path = (request.path || []).concat(pathParameter); - } else if (parameter.in === 'header') { - const headerParameter = translateToHeaderParam(document, parameter); - request.headers = (request.headers || []).concat(headerParameter); + for (const param of parameters) { + if (isQueryParam(param)) { + params.query.push(translateToQueryParameter.call(this, param)); + } else if (isPathParam(param)) { + params.path.push(translateToPathParameter.call(this, param)); + } else if (isHeaderParam(param)) { + params.headers.push(translateToHeaderParam.call(this, param)); } - return request; + } + + return { + ...params, + + ...pickBy( + { + body, + }, + isNonNullable, + ), }; -} +}; diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index 78592e70..34b58bd8 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,34 +1,34 @@ -import type { DeepPartial, Dictionary, IHttpOperationResponse, Optional } from '@stoplight/types'; -import { chain, compact, map, partial } from 'lodash'; -import type { Spec } from 'swagger-schema-official'; +import { isPlainObject } from '@stoplight/json'; +import type { IHttpOperationResponse, Optional } from '@stoplight/types'; +import { DeepPartial } from '@stoplight/types'; +import { Operation } from 'swagger-schema-official'; +import { isNonNullable } from '../../guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import { entries } from '../../utils'; +import { getExamplesFromSchema, getProduces } from '../accessors'; import { isResponseObject } from '../guards'; -import { getExamplesFromSchema } from './getExamplesFromSchema'; +import { Oas2TranslateFunction } from '../types'; import { translateToHeaderParams } from './params'; -function translateToResponse( - document: DeepPartial, - produces: string[], - response: unknown, - statusCode: string, -): Optional { - const resolvedResponse = maybeResolveLocalRef(document, response); +const translateToResponse: Oas2TranslateFunction< + [produces: string[], statusCode: string, response: unknown], + Optional +> = function (produces, statusCode, response) { + const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; - const headers = translateToHeaderParams(document, resolvedResponse.headers || {}); - const objectifiedExamples = chain( + const headers = translateToHeaderParams.call(this, resolvedResponse.headers); + const objectifiedExamples = entries( resolvedResponse.examples || (resolvedResponse.schema ? getExamplesFromSchema(resolvedResponse.schema) : void 0), - ) - .mapValues((value, key) => ({ key, value })) - .values() - .value(); + ).map(([key, value]) => ({ key, value })); const contents = produces .map(produceElement => ({ mediaType: produceElement, - schema: isDictionary(resolvedResponse.schema) ? translateSchemaObject(document, resolvedResponse.schema) : void 0, + schema: isPlainObject(resolvedResponse.schema) + ? translateSchemaObject.call(this, resolvedResponse.schema) + : void 0, examples: objectifiedExamples.filter(example => example.key === produceElement), })) .filter(({ schema, examples }) => !!schema || examples.length > 0); @@ -53,21 +53,14 @@ function translateToResponse( } return translatedResponses; -} +}; -export function translateToResponses( - document: DeepPartial, - responses: unknown, - produces: string[], -): IHttpOperationResponse[] { - if (!isDictionary(responses)) { - return []; - } - - return compact( - map, Optional>( - responses, - partial(translateToResponse, document, produces), - ), - ); -} +export const translateToResponses: Oas2TranslateFunction< + [operation: DeepPartial], + IHttpOperationResponse[] +> = function (operation) { + const produces = getProduces(this.document, operation); + return entries(operation.responses) + .map(([statusCode, response]) => translateToResponse.call(this, produces, statusCode, response)) + .filter(isNonNullable); +}; diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index aaf49c37..939fdfd4 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -1,141 +1,153 @@ -import { +import { isPlainObject } from '@stoplight/json'; +import type { DeepPartial, - Dictionary, - HttpSecurityScheme, IApiKeySecurityScheme, IBasicSecurityScheme, IOauth2SecurityScheme, IOauthFlowObjects, + Optional, } from '@stoplight/types'; -import { isString, pickBy } from 'lodash'; -import { +import pickBy = require('lodash.pickby'); +import type { ApiKeySecurity, + BasicAuthenticationSecurity, OAuth2AccessCodeSecurity, OAuth2ApplicationSecurity, OAuth2ImplicitSecurity, OAuth2PasswordSecurity, - Security, - Spec, } from 'swagger-schema-official'; +import { isNonNullable, isString } from '../../guards'; import { SecurityWithKey } from '../../oas3/accessors'; -import { isDictionary } from '../../utils'; import { getSecurities } from '../accessors'; import { isSecurityScheme } from '../guards'; +import { Oas2TranslateFunction } from '../types'; + +export const translateToFlows: Oas2TranslateFunction<[security: Record], IOauthFlowObjects> = + function (security) { + const flows: IOauthFlowObjects = {}; + + const scopes = isPlainObject(security.scopes) ? pickBy(security.scopes, isString) : {}; + const authorizationUrl = + 'authorizationUrl' in security && typeof security.authorizationUrl === 'string' ? security.authorizationUrl : ''; + const tokenUrl = 'tokenUrl' in security && typeof security.tokenUrl === 'string' ? security.tokenUrl : ''; + + if (security.flow === 'implicit') { + flows.implicit = { + authorizationUrl, + scopes, + }; + } else if (security.flow === 'password') { + flows.password = { + tokenUrl, + scopes, + }; + } else if (security.flow === 'application') { + flows.clientCredentials = { + tokenUrl, + scopes, + }; + } else if (security.flow === 'accessCode') { + flows.authorizationCode = { + authorizationUrl, + tokenUrl, + scopes, + }; + } -/** - * @param security the union with 'any' is purposeful. Passing strict types does not help much here, - * because all these checks happen in runtime. I'm leaving 'Security' only for visibility. - */ -function translateToFlows(security: Dictionary): IOauthFlowObjects { - const flows: IOauthFlowObjects = {}; - - const scopes = isDictionary(security.scopes) ? pickBy(security.scopes, isString) : {}; - const authorizationUrl = - 'authorizationUrl' in security && typeof security.authorizationUrl === 'string' ? security.authorizationUrl : ''; - const tokenUrl = 'tokenUrl' in security && typeof security.tokenUrl === 'string' ? security.tokenUrl : ''; - - if (security.flow === 'implicit') { - flows.implicit = { - authorizationUrl, - scopes, - }; - } else if (security.flow === 'password') { - flows.password = { - tokenUrl, - scopes, - }; - } else if (security.flow === 'application') { - flows.clientCredentials = { - tokenUrl, - scopes, - }; - } else if (security.flow === 'accessCode') { - flows.authorizationCode = { - authorizationUrl, - tokenUrl, - scopes, - }; - } - - return flows; -} + return flows; + }; -function translateToBasicSecurityScheme(security: DeepPartial, key: string): IBasicSecurityScheme { +export const translateToBasicSecurityScheme: Oas2TranslateFunction< + [security: DeepPartial & { key: string }], + IBasicSecurityScheme +> = function (security) { return { type: 'http', scheme: 'basic', - description: security.description, - key, + key: security.key, + + ...pickBy( + { + description: security.description, + }, + isString, + ), }; -} +}; -function translateToApiKeySecurityScheme( - security: DeepPartial, - key: string, -): IApiKeySecurityScheme | undefined { - const acceptableSecurityOrigins: ApiKeySecurity['in'][] = ['query', 'header']; +const ACCEPTABLE_SECURITY_ORIGINS: ApiKeySecurity['in'][] = ['query', 'header']; - if ('in' in security && security.in && acceptableSecurityOrigins.includes(security.in)) { +export const translateToApiKeySecurityScheme: Oas2TranslateFunction< + [security: DeepPartial & { key: string }], + Optional +> = function (security) { + if ('in' in security && security.in && ACCEPTABLE_SECURITY_ORIGINS.includes(security.in)) { return { type: 'apiKey', - name: security.name || '', in: security.in, - description: security.description, - key, + name: String(security.name || ''), + key: security.key, + + ...pickBy( + { + description: security.description, + }, + isString, + ), }; } - return undefined; -} + return; +}; const VALID_OAUTH2_FLOWS = ['implicit', 'password', 'application', 'accessCode']; -function translateToOauth2SecurityScheme( - security: DeepPartial< - OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity - >, - key: string, -): IOauth2SecurityScheme | undefined { +export const translateToOauth2SecurityScheme: Oas2TranslateFunction< + [ + security: DeepPartial< + OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity + > & { key: string }, + ], + Optional +> = function (security) { if (!security.flow || !VALID_OAUTH2_FLOWS.includes(security.flow)) return undefined; return { type: 'oauth2', - flows: translateToFlows(security), - description: security.description, - key, + flows: translateToFlows.call(this, security), + key: security.key, + + ...pickBy( + { + description: security.description, + }, + isString, + ), }; -} +}; -export function translateToSingleSecurity(security: unknown, key: string) { +export const translateToSingleSecurity: Oas2TranslateFunction< + [security: unknown & { key: string }], + Optional +> = function (security) { if (isSecurityScheme(security)) { switch (security.type) { case 'basic': - return translateToBasicSecurityScheme(security, key); + return translateToBasicSecurityScheme.call(this, security); case 'apiKey': - return translateToApiKeySecurityScheme(security, key); + return translateToApiKeySecurityScheme.call(this, security); case 'oauth2': - return translateToOauth2SecurityScheme(security, key); + return translateToOauth2SecurityScheme.call(this, security); } } return; -} - -export function translateToSecurities( - document: DeepPartial, - operationSecurity: Dictionary[] | undefined, -): HttpSecurityScheme[][] { - const securities = getSecurities(document, operationSecurity); - - return securities.map(security => - security.reduce((transformedSecurities, sec) => { - const transformed = translateToSingleSecurity(sec, sec.key); - if (transformed) { - transformedSecurities.push(transformed); - } - - return transformedSecurities; - }, []), - ); -} +}; + +export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = + function (operationSecurities) { + const securities = getSecurities(this.document, operationSecurities); + + return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); + }; diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index 2dee9fd6..7be41c5d 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,32 +1,52 @@ -import { DeepPartial, IServer } from '@stoplight/types'; -import { Operation, Spec } from 'swagger-schema-official'; +import type { IServer, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { isNonNullable, isString } from '../../guards'; import { isValidScheme } from '../guards'; +import type { Oas2TranslateFunction } from '../types'; -export function translateToServers(spec: DeepPartial, operation: DeepPartial): IServer[] { - const { host } = spec; - if (typeof host !== 'string' || host.length === 0) { +export const translateToServers: Oas2TranslateFunction<[operation: Record], IServer[]> = function ( + operation, +) { + let schemes; + if (Array.isArray(operation.schemes)) { + schemes = operation.schemes; + } else if (Array.isArray(this.document.schemes)) { + schemes = this.document.schemes; + } else { return []; } - const schemes = operation.schemes || spec.schemes; - if (!Array.isArray(schemes)) { - return []; + return schemes.map(translateToServer, this).filter(isNonNullable); +}; + +export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optional> = function (scheme) { + const { host } = this.document; + if (typeof host !== 'string' || host.length === 0) { + return; } - const basePath = typeof spec.basePath === 'string' && spec.basePath.length > 0 ? spec.basePath : null; + if (!isString(scheme) || !isValidScheme(scheme)) return; - return schemes.filter(isValidScheme).map(scheme => { - const uri = new URL('https://localhost'); - uri.host = host; - uri.protocol = `${scheme}:`; + const basePath = + typeof this.document.basePath === 'string' && this.document.basePath.length > 0 ? this.document.basePath : null; + + const uri = new URL('https://localhost'); + uri.host = host; + uri.protocol = `${scheme}:`; + + if (basePath !== null) { + uri.pathname = basePath; + } - if (basePath !== null) { - uri.pathname = basePath; - } + return { + url: uri.toString().replace(/\/$/, ''), // Remove trailing slash - return { - url: uri.toString().replace(/\/$/, ''), // Remove trailing slash - }; - }); -} + ...pickBy( + { + name: this.document.info?.title, + }, + isString, + ), + }; +}; diff --git a/src/oas2/types.ts b/src/oas2/types.ts new file mode 100644 index 00000000..dcfe7a17 --- /dev/null +++ b/src/oas2/types.ts @@ -0,0 +1,10 @@ +import type { DeepPartial } from '@stoplight/types'; +import type { Spec } from 'swagger-schema-official'; + +import { TranslateFunction } from '../types'; + +export type Oas2TranslateFunction

= TranslateFunction< + DeepPartial, + P, + R +>; diff --git a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap index b9e22450..92aa3bb3 100644 --- a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap @@ -48,8 +48,6 @@ Object { }, }, ], - "description": undefined, - "required": undefined, }, "cookie": Array [], "headers": Array [], @@ -111,9 +109,8 @@ Object { "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "title", "url": "operation/server", - "variables": undefined, }, ], "summary": "summary", @@ -143,9 +140,8 @@ Object { "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "title", "url": "path/server", - "variables": undefined, }, ], "summary": "summary", @@ -205,9 +201,8 @@ Object { "security": Array [], "servers": Array [ Object { - "description": undefined, + "name": "title", "url": "spec/server", - "variables": undefined, }, ], "summary": "summary", diff --git a/src/oas3/__tests__/operation.test.ts b/src/oas3/__tests__/operation.test.ts index 7152e034..6219e040 100644 --- a/src/oas3/__tests__/operation.test.ts +++ b/src/oas3/__tests__/operation.test.ts @@ -339,7 +339,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - description: void 0, + name: 'title', url: 'operation/server', }, ]); @@ -437,7 +437,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - description: void 0, + name: 'title', url: 'path/server', }, ]); @@ -535,7 +535,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { - description: void 0, + name: 'title', url: 'spec/server', }, ]); @@ -642,6 +642,7 @@ describe('transformOas3Operation', () => { }, ], name: 'name', + style: 'simple', schema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', @@ -718,6 +719,7 @@ describe('transformOas3Operation', () => { value: 'some example', }, ], + style: 'simple', name: 'name', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -787,18 +789,14 @@ describe('transformOas3Operation', () => { variables: { basePath: { default: 'v2', - description: void 0, - enum: void 0, }, port: { default: '8443', - description: void 0, enum: ['8443', '443'], }, username: { default: 'demo', description: 'value is assigned by the service provider', - enum: void 0, }, }, }, @@ -899,9 +897,7 @@ describe('transformOas3Operation', () => { encodings: [], examples: [ { - description: undefined, key: 'pet-shared', - summary: undefined, value: { type: 'object', properties: { @@ -913,12 +909,10 @@ describe('transformOas3Operation', () => { }, }, { - description: undefined, key: 'pet-not-shared', value: { 'not-shared': true, }, - summary: undefined, }, ], mediaType: 'application/json', @@ -929,8 +923,6 @@ describe('transformOas3Operation', () => { }, }, ], - description: undefined, - required: undefined, }, cookie: [], headers: [], @@ -945,9 +937,7 @@ describe('transformOas3Operation', () => { encodings: [], examples: [ { - description: undefined, key: 'pet-shared', - summary: undefined, value: { properties: { id: { @@ -959,9 +949,7 @@ describe('transformOas3Operation', () => { }, }, { - description: undefined, key: 'pet-not-shared', - summary: undefined, value: { 'not-shared': true, }, @@ -975,7 +963,6 @@ describe('transformOas3Operation', () => { }, }, ], - description: undefined, headers: [], }, ], @@ -1251,30 +1238,25 @@ describe('transformOas3Operation', () => { request: { body: { contents: [ - { - encodings: [], - examples: [], - mediaType: 'application/json', + expect.objectContaining({ schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }, + }), ], }, cookie: [], headers: [ - { - examples: [{ key: 'default', value: 'test' }], - name: 'email', + expect.objectContaining({ schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'string', format: 'email', example: 'test', }, - }, + }), ], path: [], query: [], @@ -1283,16 +1265,13 @@ describe('transformOas3Operation', () => { { code: '200', contents: [ - { - encodings: [], - examples: [], - mediaType: 'application/json', + expect.objectContaining({ schema: { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }, + }), ], headers: [], }, diff --git a/src/oas3/__tests__/service.test.ts b/src/oas3/__tests__/service.test.ts index 01ea45b9..523ac541 100644 --- a/src/oas3/__tests__/service.test.ts +++ b/src/oas3/__tests__/service.test.ts @@ -115,12 +115,10 @@ describe('oas3 service', () => { version: '1.0', servers: [ { - description: void 0, name: '', url: 'https://petstore.swagger.io/v2', }, { - description: void 0, name: '', url: 'http://petstore.swagger.io/v2', }, @@ -180,29 +178,23 @@ describe('oas3 service', () => { servers: [ { description: 'Sample Petstore Server Https', - name: '', url: 'https://petstore.swagger.io/v2', variables: { basePath: { default: 'v2', - description: void 0, - enum: void 0, }, port: { default: '8443', - description: void 0, enum: ['8443', '443'], }, username: { default: 'demo', description: 'value is assigned by the service provider', - enum: void 0, }, }, }, { description: 'Sample Petstore Server Http', - name: '', url: 'http://petstore.swagger.io/v2', }, ], diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index c637efff..38f66cf5 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,7 +1,9 @@ +import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, Dictionary, HttpSecurityScheme } from '@stoplight/types'; -import { isObject, mapValues, pickBy } from 'lodash'; -import { OAuthFlowObject, OpenAPIObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import type { OpenAPIObject } from 'openapi3-ts'; +import { entries } from '../utils'; import { isSecurityScheme, isSecuritySchemeWithKey } from './guards'; export type OperationSecurities = Dictionary[] | undefined; @@ -9,14 +11,14 @@ export type SecurityWithKey = HttpSecurityScheme & { key: string }; export function getSecurities( document: DeepPartial, - operationSecurities?: OperationSecurities, + operationSecurities?: unknown, ): SecurityWithKey[][] { const definitions = document.components?.securitySchemes; - if (!isObject(definitions)) return []; + if (!isPlainObject(definitions)) return []; - return (operationSecurities || document.security || []).map(operationSecurity => { - return Object.entries(operationSecurity) + return (Array.isArray(operationSecurities) ? operationSecurities : document.security || []).map(operationSecurity => { + return entries(operationSecurity) .map(([opScheme, scopes]) => { const definition = definitions[opScheme]; @@ -24,10 +26,15 @@ export function getSecurities( // Put back only the flows that are part of the current definition return { ...definition, - flows: mapValues(definition.flows, (flow: OAuthFlowObject) => ({ - ...flow, - scopes: pickBy(flow.scopes, (_val: string, key: string) => scopes?.includes(key)), - })), + flows: Object.fromEntries( + entries(definition.flows).map(([name, flow]) => [ + name, + { + ...flow, + scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), + }, + ]), + ), key: opScheme, }; } diff --git a/src/oas3/guards.ts b/src/oas3/guards.ts index c2cdf920..3bac2280 100644 --- a/src/oas3/guards.ts +++ b/src/oas3/guards.ts @@ -1,6 +1,5 @@ -import { Dictionary } from '@stoplight/types'; -import { isObject } from 'lodash'; -import { +import { isPlainObject } from '@stoplight/json'; +import type { BaseParameterObject, HeaderObject, OAuthFlowObject, @@ -12,19 +11,18 @@ import { TagObject, } from 'openapi3-ts'; -import { isDictionary } from '../utils'; -import { SecurityWithKey } from './accessors'; +import type { SecurityWithKey } from './accessors'; export const isSecurityScheme = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecuritySchemeObject => - isObject(maybeSecurityScheme) && typeof (maybeSecurityScheme as Dictionary).type === 'string'; + isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; export const isSecuritySchemeWithKey = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecurityWithKey => - isSecurityScheme(maybeSecurityScheme) && typeof (maybeSecurityScheme as Dictionary).key === 'string'; + isSecurityScheme(maybeSecurityScheme) && typeof maybeSecurityScheme.key === 'string'; export const isBaseParameterObject = ( maybeBaseParameterObject: unknown, ): maybeBaseParameterObject is BaseParameterObject => - isObject(maybeBaseParameterObject) && + isPlainObject(maybeBaseParameterObject) && ('description' in maybeBaseParameterObject || 'required' in maybeBaseParameterObject || 'content' in maybeBaseParameterObject || @@ -38,33 +36,33 @@ export const isHeaderObject = (maybeHeaderObject: unknown): maybeHeaderObject is isBaseParameterObject(maybeHeaderObject); export const isServerObject = (maybeServerObject: unknown): maybeServerObject is ServerObject => - isObject(maybeServerObject) && typeof (maybeServerObject as Dictionary).url === 'string'; + isPlainObject(maybeServerObject) && typeof maybeServerObject.url === 'string'; export const isServerVariableObject = ( maybeServerVariableObject: unknown, ): maybeServerVariableObject is ServerVariableObject => { - if (!isObject(maybeServerVariableObject)) return false; - const typeofDefault = typeof (maybeServerVariableObject as Dictionary).default; + if (!isPlainObject(maybeServerVariableObject)) return false; + const typeofDefault = typeof maybeServerVariableObject.default; return typeofDefault === 'string' || typeofDefault === 'boolean' || typeofDefault === 'number'; }; export const isTagObject = (maybeTagObject: unknown): maybeTagObject is TagObject => { - if (isObject(maybeTagObject) && 'name' in maybeTagObject) { - return typeof (maybeTagObject as TagObject).name === 'string'; + if (isPlainObject(maybeTagObject) && 'name' in maybeTagObject) { + return typeof maybeTagObject.name === 'string'; } return false; }; export const isResponseObject = (maybeResponseObject: unknown): maybeResponseObject is ResponseObject => - isObject(maybeResponseObject) && + isPlainObject(maybeResponseObject) && ('description' in maybeResponseObject || 'headers' in maybeResponseObject || 'content' in maybeResponseObject || 'links' in maybeResponseObject); export const isOAuthFlowObject = (maybeOAuthFlowObject: unknown): maybeOAuthFlowObject is OAuthFlowObject => - isDictionary(maybeOAuthFlowObject) && isDictionary(maybeOAuthFlowObject.scopes); + isPlainObject(maybeOAuthFlowObject) && isPlainObject(maybeOAuthFlowObject.scopes); export const isRequestBodyObject = (maybeRequestBodyObject: unknown): maybeRequestBodyObject is RequestBodyObject => - isDictionary(maybeRequestBodyObject) && isDictionary(maybeRequestBodyObject.content); + isPlainObject(maybeRequestBodyObject) && isPlainObject(maybeRequestBodyObject.content); diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index 28617ada..f423a26c 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,13 +1,14 @@ import { IHttpOperation } from '@stoplight/types'; -import { get, isNil, omitBy } from 'lodash'; -import type { OpenAPIObject, OperationObject, ParameterObject, PathsObject, RequestBodyObject } from 'openapi3-ts'; +import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import { createContext } from '../context'; +import { isNonNullable } from '../guards'; import { transformOasOperations } from '../oas'; -import { getExtensions, getOasTags, getValidOasParameters } from '../oas/accessors'; -import { translateToTags } from '../oas/tag'; -import { Oas3HttpOperationTransformer } from '../oas/types'; +import { getExtensions } from '../oas/accessors'; +import { translateToTags } from '../oas/tags'; +import type { Oas3HttpOperationTransformer } from '../oas/types'; import { maybeResolveLocalRef } from '../utils'; -import { isServerObject } from './guards'; import { translateToCallbacks } from './transformers/callbacks'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; @@ -19,7 +20,7 @@ export function transformOas3Operations(document: OpenAPIObject): IHttpOperation } export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, get(document, ['paths', path])) as PathsObject; + const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]) as PathsObject; if (typeof pathObj !== 'object' || pathObj === null) { throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); } @@ -29,7 +30,7 @@ export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); } - const servers = operation.servers || pathObj.servers || document.servers; + const ctx = createContext(document); const httpOperation: IHttpOperation = { id: '?http-operation-id?', @@ -40,18 +41,14 @@ export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, method, path, summary: operation.summary, - responses: translateToResponses(document, operation.responses), - servers: Array.isArray(servers) ? translateToServers(servers.filter(isServerObject)) : [], - request: translateToRequest( - document, - getValidOasParameters(document, operation.parameters as ParameterObject[], pathObj.parameters), - operation.requestBody as RequestBodyObject, - ), - callbacks: operation.callbacks && translateToCallbacks(operation.callbacks), - tags: translateToTags(getOasTags(operation.tags)), - security: translateToSecurities(document, operation.security), + responses: translateToResponses.call(ctx, operation.responses), + request: translateToRequest.call(ctx, pathObj, operation), + callbacks: translateToCallbacks.call(ctx, operation.callbacks), + tags: translateToTags.call(ctx, operation.tags), + security: translateToSecurities.call(ctx, operation.security), extensions: getExtensions(operation), + servers: translateToServers.call(ctx, pathObj, operation), }; - return omitBy(httpOperation, isNil) as IHttpOperation; + return pickBy(httpOperation, isNonNullable) as IHttpOperation; }; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index 9626116e..acce098f 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,34 +1,27 @@ -import { HttpSecurityScheme, IHttpService, IServer } from '@stoplight/types'; -import { compact, filter, flatMap, keys, map, pickBy } from 'lodash'; - -import { hasXLogo } from '../oas/guards'; -import { translateLogo } from '../oas/transformers/translateLogo'; -import { Oas3HttpServiceTransformer } from '../oas/types'; -import { isDictionary } from '../utils'; -import { isSecurityScheme, isTagObject } from './guards'; -import { transformToSingleSecurity } from './transformers/securities'; -import { translateServerVariables } from './transformers/servers'; +import { isPlainObject } from '@stoplight/json'; +import type { Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import { createContext } from '../context'; +import { isNonNullable } from '../guards'; +import { transformOasService } from '../oas/service'; +import type { Oas3HttpServiceTransformer } from '../oas/types'; +import { ArrayCallbackParameters } from '../types'; +import { entries } from '../utils'; +import { SecurityWithKey } from './accessors'; +import { isSecurityScheme } from './guards'; +import { translateToSingleSecurity } from './transformers/securities'; +import { translateToServer } from './transformers/servers'; +import { Oas3TranslateFunction } from './types'; export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) => { - const httpService: IHttpService = { - id: '?http-service-id?', - version: document.info?.version ?? '', - name: document.info?.title ?? 'no-title', - }; - - if (typeof document.info?.description === 'string') { - httpService.description = document.info.description; - } + const ctx = createContext(document); + const httpService = transformOasService.call(ctx); if (typeof document.info?.summary === 'string') { httpService.summary = document.info.summary; } - if (document.info?.contact) { - httpService.contact = document.info.contact; - } - - if (typeof document.info?.license === 'object' && document.info.license !== null) { + if (document.info?.license) { const { name, identifier, ...license } = document.info.license; httpService.license = { ...license, @@ -37,50 +30,27 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = }; } - if (document.info?.termsOfService) { - httpService.termsOfService = document.info.termsOfService; - } - - if (isDictionary(document.info) && hasXLogo(document.info)) { - httpService.logo = translateLogo(document.info); - } - - const servers = compact( - map(document.servers, server => { - if (!server) return null; + const servers = Array.isArray(document.servers) + ? document.servers.map(translateToServer, ctx).filter(isNonNullable) + : []; - const serv: IServer = { - name: document.info?.title ?? '', - description: server.description, - url: server.url ?? '', - }; - - const variables = server.variables && translateServerVariables(server.variables); - if (variables && Object.keys(variables).length) serv.variables = variables; - - return serv; - }), - ); if (servers.length) { httpService.servers = servers; } - const securitySchemes = compact( - keys(document.components?.securitySchemes).map(key => { - const definition = document?.components?.securitySchemes?.[key]; - return isSecurityScheme(definition) && transformToSingleSecurity(definition, key); - }), - ); + const securitySchemes = entries(document.components?.securitySchemes) + .map(translateSecurityScheme, ctx) + .filter(isNonNullable); if (securitySchemes.length) { httpService.securitySchemes = securitySchemes; } - const security = compact( - flatMap(document.security, sec => { - if (!sec) return null; + const security = (Array.isArray(document.security) ? document.security : []) + .flatMap(sec => { + if (!isPlainObject(sec)) return null; - return keys(sec).map(key => { + return Object.keys(sec).map(key => { const ss = securitySchemes.find(securityScheme => securityScheme.key === key); if (ss && ss.type === 'oauth2') { const flows = {}; @@ -104,18 +74,26 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = return ss; }); - }), - ); + }) + .filter(isNonNullable); if (security.length) { - //@ts-ignore I hate doing this, but unfortunately Lodash types are (rightfully) very loose and put undefined when it can't happen httpService.security = security; } - const tags = filter(document.tags, isTagObject); - if (tags.length) { - httpService.tags = tags; + return httpService; +}; + +const translateSecurityScheme: Oas3TranslateFunction< + ArrayCallbackParameters<[name: string, scheme: unknown]>, + Optional +> = function ([key, definition]) { + if (!isSecurityScheme(definition)) return; + + const transformed = translateToSingleSecurity.call(this, definition); + if (transformed && 'key' in transformed) { + transformed.key = key; } - return httpService; + return transformed; }; diff --git a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap index 22c29f5d..d45eea44 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap @@ -4,12 +4,12 @@ exports[`translateHeaderObject should translate to IHttpHeaderParam 1`] = ` Object { "allowEmptyValue": true, "allowReserved": true, + "content": Object {}, "deprecated": true, "description": "descr", "encodings": Array [], "examples": Array [ Object { - "description": undefined, "key": "a", "summary": "example summary", "value": "hey", @@ -22,8 +22,10 @@ Object { "explode": true, "name": "header-name", "required": true, - "schema": Object {}, - "style": "matrix", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, + "style": "simple", } `; @@ -31,21 +33,22 @@ exports[`translateMediaTypeObject given complex nested media type object should Object { "encodings": Array [ Object { - "contentType": "text/plain", "headers": Array [ Object { + "content": Object { + "nested/media": Object {}, + }, "description": "descr", "encodings": Array [], "examples": Array [ Object { - "description": undefined, "key": "a", "summary": "example summary", "value": "hey", }, ], "name": "h1", - "style": "matrix", + "style": "simple", }, ], "mediaType": "text/plain", @@ -55,7 +58,6 @@ Object { ], "examples": Array [ Object { - "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -68,40 +70,11 @@ Object { } `; -exports[`translateMediaTypeObject given complex nested media type object with nullish headers should translate correctly 1`] = ` -Object { - "encodings": Array [ - Object { - "contentType": "text/plain", - "headers": Array [], - "mediaType": "text/plain", - "property": "enc1", - "style": "form", - }, - ], - "examples": Array [ - Object { - "description": undefined, - "key": "example", - "summary": "multi example", - "value": "hey", - }, - ], - "mediaType": "mediaType", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, -} -`; - -exports[`translateMediaTypeObject given encoding with incorrect style should throw an error 1`] = `"Encoding property style: 'xyz' is incorrect, must be one of: form,spaceDelimited,pipeDelimited,deepObject"`; - exports[`translateMediaTypeObject given encodings should translate each encoding to array item 1`] = ` Object { "encodings": Array [ Object { "allowReserved": true, - "contentType": "text/plain", "explode": true, "headers": Array [ Object { @@ -117,7 +90,6 @@ Object { }, Object { "allowReserved": true, - "contentType": "text/plain", "explode": true, "headers": Array [ Object { @@ -134,7 +106,6 @@ Object { ], "examples": Array [ Object { - "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -152,7 +123,6 @@ Object { "encodings": Array [], "examples": Array [ Object { - "description": undefined, "key": "example", "summary": "multi example", "value": "hey", @@ -180,3 +150,27 @@ Object { }, } `; + +exports[`translateMediaTypeObject should skip nullish headers 1`] = ` +Object { + "encodings": Array [ + Object { + "headers": Array [], + "mediaType": "text/plain", + "property": "enc1", + "style": "form", + }, + ], + "examples": Array [ + Object { + "key": "example", + "summary": "multi example", + "value": "hey", + }, + ], + "mediaType": "mediaType", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + }, +} +`; diff --git a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap index 164c80f4..a987deb6 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -4,7 +4,15 @@ exports[`translateOas3ToRequest give a request body should translate it 1`] = ` Object { "body": Object { "contents": Array [ - "translateMediaTypeObject({}, {\\"schema\\":{\\"deprecated\\":true}}, content-a, {\\"content-a\\":{\\"schema\\":{\\"deprecated\\":true}}})", + Object { + "encodings": Array [], + "examples": Array [], + "mediaType": "content-a", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "deprecated": true, + }, + }, ], "description": "descr", "required": true, @@ -32,30 +40,33 @@ Object { "description": "descr", "examples": Array [], "name": "param-name-3", + "style": "simple", }, ], "path": Array [], "query": Array [ Object { "content": Object { - "content-a": Object { + "content-b": Object { "schema": Object {}, }, }, - "deprecated": true, "description": "descr", "examples": Array [], - "name": "param-name-1", + "name": "param-name-2", + "style": "simple", }, Object { "content": Object { - "content-b": Object { + "content-a": Object { "schema": Object {}, }, }, + "deprecated": true, "description": "descr", "examples": Array [], - "name": "param-name-2", + "name": "param-name-1", + "style": "simple", }, ], } diff --git a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap index 90fa85a6..ef2bed75 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -14,7 +14,6 @@ Array [ }, ], "mediaType": "fake-content-type-200", - "schema": undefined, }, ], "description": "descr 200", @@ -34,7 +33,6 @@ Array [ "encodings": Array [], "examples": Array [], "mediaType": "fake-content-type", - "schema": undefined, }, ], "description": "descr", @@ -50,7 +48,10 @@ Array [ ], "name": "fake-header-name-1", "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, "type": "integer", }, "style": "simple", @@ -67,7 +68,10 @@ Array [ "name": "fake-header-name-2", "required": true, "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, "type": "integer", }, "style": "simple", diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index 05fef5b7..d22bf44a 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -1,6 +1,16 @@ -import { SchemaObject } from 'openapi3-ts'; +import type { SchemaObject } from 'openapi3-ts'; -import { translateHeaderObject, translateMediaTypeObject } from '../content'; +import { createContext } from '../../../context'; +import { + translateHeaderObject as _translateHeaderObject, + translateMediaTypeObject as _translateMediaTypeObject, +} from '../content'; + +const translateMediaTypeObject = (document: any, object: unknown, key: string) => + _translateMediaTypeObject.call(createContext(document), [key, object], 0, []); + +const translateHeaderObject = (object: unknown, key: string) => + _translateHeaderObject.call(createContext({}), [key, object], 0, []); describe('translateMediaTypeObject', () => { afterEach(() => { @@ -16,28 +26,6 @@ describe('translateMediaTypeObject', () => { encodings: [], examples: [], mediaType: 'mediaType', - schema: void 0, - }); - }); - - it('given invalid schema, should return nothing', () => { - expect( - translateMediaTypeObject( - {}, - { - schema: { - get properties() { - throw new Error('I am invalid'); - }, - }, - }, - 'mediaType', - ), - ).toStrictEqual({ - encodings: [], - examples: [], - mediaType: 'mediaType', - schema: void 0, }); }); @@ -54,7 +42,6 @@ describe('translateMediaTypeObject', () => { encodings: [], examples: [], mediaType: 'mediaType', - schema: void 0, }); }); @@ -159,7 +146,7 @@ describe('translateMediaTypeObject', () => { ).toMatchSnapshot(); }); - it('given complex nested media type object with nullish headers should translate correctly', () => { + it('should skip nullish headers', () => { expect( translateMediaTypeObject( {}, @@ -181,26 +168,6 @@ describe('translateMediaTypeObject', () => { ).toMatchSnapshot(); }); - it('given encoding with incorrect style should throw an error', () => { - const testedFunction = () => { - translateMediaTypeObject( - {}, - { - schema: {}, - examples: { example: { summary: 'multi example' } }, - encoding: { - enc1: { - contentType: 'text/plain', - style: 'xyz', - }, - }, - }, - 'mediaType', - ); - }; - expect(testedFunction).toThrowErrorMatchingSnapshot(); - }); - it('given encoding with no style it should not throw an error', () => { const testedFunction = () => { translateMediaTypeObject( diff --git a/src/oas3/transformers/__tests__/request.test.ts b/src/oas3/transformers/__tests__/request.test.ts index bc7bc201..94b8f51c 100644 --- a/src/oas3/transformers/__tests__/request.test.ts +++ b/src/oas3/transformers/__tests__/request.test.ts @@ -1,56 +1,61 @@ -import { mockPassthroughImplementation } from '@stoplight/test-utils'; +import { createContext } from '../../../context'; +import { translateToRequest as _translateToRequest } from '../request'; -import { translateMediaTypeObject } from '../content'; -import { translateToRequest } from '../request'; - -jest.mock('../content'); +const translateToRequest = (path: Record, operation: Record) => { + const ctx = createContext({ paths: { '/api': path } }); + return _translateToRequest.call(ctx, path, operation); +}; describe('translateOas3ToRequest', () => { - beforeEach(() => { - mockPassthroughImplementation(translateMediaTypeObject); - }); - it('given no request body should translate parameters', () => { - expect( - translateToRequest({}, [ + const operation = { + parameters: [ { - name: 'param-name-1', + name: 'param-name-2', in: 'query', description: 'descr', - deprecated: true, content: { - 'content-a': { + 'content-b': { schema: {}, }, }, }, { - name: 'param-name-2', - in: 'query', + name: 'param-name-3', + in: 'header', description: 'descr', content: { - 'content-b': { + 'content-c': { schema: {}, }, }, }, + ], + }; + + const path = { + parameters: [ { - name: 'param-name-3', - in: 'header', + name: 'param-name-1', + in: 'query', description: 'descr', + deprecated: true, content: { - 'content-c': { + 'content-a': { schema: {}, }, }, }, - ]), - ).toMatchSnapshot(); + ], + get: operation, + }; + + expect(translateToRequest(path, operation)).toMatchSnapshot(); }); it('give a request body should translate it', () => { - expect( - translateToRequest({}, [], { + const operation = { + requestBody: { description: 'descr', required: true, content: { @@ -60,7 +65,13 @@ describe('translateOas3ToRequest', () => { }, }, }, - }), - ).toMatchSnapshot(); + }, + }; + + const path = { + post: operation, + }; + + expect(translateToRequest(path, operation)).toMatchSnapshot(); }); }); diff --git a/src/oas3/transformers/__tests__/responses.test.ts b/src/oas3/transformers/__tests__/responses.test.ts index 0d8154fd..fe04ccc0 100644 --- a/src/oas3/transformers/__tests__/responses.test.ts +++ b/src/oas3/transformers/__tests__/responses.test.ts @@ -1,7 +1,11 @@ import type { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { translateToResponses } from '../responses'; +import { createContext } from '../../../context'; +import { translateToResponses as _translateToResponses } from '../responses'; + +const translateToResponses = (document: DeepPartial, responses: unknown) => + _translateToResponses.call(createContext(document), responses); describe('translateToOas3Responses', () => { it('given empty dictionary should return empty array', () => { @@ -70,7 +74,6 @@ describe('translateToOas3Responses', () => { { code: '200', contents: [], - description: void 0, headers: [], }, ]); @@ -212,6 +215,7 @@ describe('translateToOas3Responses', () => { { name: 'X-Page', schema: { + $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', }, style: 'simple', diff --git a/src/oas3/transformers/__tests__/securities.test.ts b/src/oas3/transformers/__tests__/securities.test.ts index 0bf47a08..b682c291 100644 --- a/src/oas3/transformers/__tests__/securities.test.ts +++ b/src/oas3/transformers/__tests__/securities.test.ts @@ -1,6 +1,12 @@ +import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { translateToSecurities } from '../securities'; +import { createContext } from '../../../context'; +import { OperationSecurities } from '../../accessors'; +import { translateToSecurities as _translateToSecurities } from '../securities'; + +const translateToSecurities = (document: DeepPartial, operationSecurities: OperationSecurities) => + _translateToSecurities.call(createContext(document), operationSecurities); describe('securities', () => { describe('translateToSecurities', () => { diff --git a/src/oas3/transformers/__tests__/servers.test.ts b/src/oas3/transformers/__tests__/servers.test.ts index 3e396d77..d6573d06 100644 --- a/src/oas3/transformers/__tests__/servers.test.ts +++ b/src/oas3/transformers/__tests__/servers.test.ts @@ -1,9 +1,17 @@ -import { translateToServers } from '../servers'; +import { DeepPartial } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; + +import { createContext } from '../../../context'; +import { translateToServers as _translateToServers } from '../servers'; + +const translateToServers = ( + document: DeepPartial & { paths: { '/pet': { get: Record } } }, +) => _translateToServers.call(createContext(document), document.paths['/pet'], document.paths['/pet'].get); describe('translateToServers', () => { it('translate single ServerObject to IServer', () => { - expect( - translateToServers([ + const document = { + servers: [ { description: 'description', url: 'http://stoplight.io/path', @@ -20,13 +28,20 @@ describe('translateToServers', () => { }, }, }, - ]), - ).toMatchSnapshot(); + ], + paths: { + '/pet': { + get: {}, + }, + }, + }; + + expect(translateToServers(document)).toMatchSnapshot(); }); it('filters out invalid variables', () => { - expect( - translateToServers([ + const document = { + servers: [ { description: 'description', url: 'http://stoplight.io/path', @@ -43,8 +58,15 @@ describe('translateToServers', () => { }, } as any, }, - ]), - ).toStrictEqual([ + ], + paths: { + '/pet': { + get: {}, + }, + }, + }; + + expect(translateToServers(document)).toStrictEqual([ { description: 'description', url: 'http://stoplight.io/path', @@ -58,4 +80,71 @@ describe('translateToServers', () => { }, ]); }); + + it('prefers operation servers over any other servers', () => { + const document = { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/path', + }, + ], + paths: { + '/pet': { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/pet', + }, + ], + + get: { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/pet.get', + }, + ], + }, + }, + }, + }; + + expect(translateToServers(document)).toStrictEqual([ + { + description: 'description', + url: 'http://stoplight.io/pet.get', + }, + ]); + }); + + it('given missing operation servers, prefers path servers over any document servers', () => { + const document = { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/path', + }, + ], + paths: { + '/pet': { + servers: [ + { + description: 'description', + url: 'http://stoplight.io/pet', + }, + ], + + get: {}, + }, + }, + }; + + expect(translateToServers(document)).toStrictEqual([ + { + description: 'description', + url: 'http://stoplight.io/pet', + }, + ]); + }); }); diff --git a/src/oas3/transformers/callbacks.ts b/src/oas3/transformers/callbacks.ts index c97e4bf5..422cdbd8 100644 --- a/src/oas3/transformers/callbacks.ts +++ b/src/oas3/transformers/callbacks.ts @@ -1,33 +1,35 @@ -import { IHttpCallbackOperation } from '@stoplight/types'; -import { entries } from 'lodash'; -import { CallbacksObject, OpenAPIObject } from 'openapi3-ts'; +import type { IHttpCallbackOperation } from '@stoplight/types'; +import type { OpenAPIObject } from 'openapi3-ts'; +import { entries } from '../../utils'; import { transformOas3Operation } from '../operation'; +import type { Oas3TranslateFunction } from '../types'; -export function translateToCallbacks(callbacks: CallbacksObject): IHttpCallbackOperation[] | undefined { - const callbackEntries = entries(callbacks); - if (!callbackEntries.length) return; +export const translateToCallbacks: Oas3TranslateFunction<[callbacks: unknown], IHttpCallbackOperation[] | undefined> = + function (callbacks) { + const callbackEntries = entries(callbacks); + if (!callbackEntries.length) return; - return callbackEntries.reduce((results: IHttpCallbackOperation[], [callbackName, path2Methods]) => { - for (const [path, method2Op] of entries(path2Methods)) { - for (const [method, op] of entries(method2Op as { [key: string]: {} })) { - const document: Partial = { - openapi: '3', - info: { title: '', version: '1' }, - paths: { [path]: { [method]: op } }, - }; + return callbackEntries.reduce((results: IHttpCallbackOperation[], [callbackName, path2Methods]) => { + for (const [path, method2Op] of entries(path2Methods)) { + for (const [method, op] of entries(method2Op as { [key: string]: {} })) { + const document: Partial = { + openapi: '3', + info: { title: '', version: '1' }, + paths: { [path]: { [method]: op } }, + }; - results.push({ - ...transformOas3Operation({ - document, - method, - path, - }), - callbackName, - }); + results.push({ + ...transformOas3Operation({ + document, + method, + path, + }), + callbackName, + }); + } } - } - return results; - }, []); -} + return results; + }, []); + }; diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index 696e31a1..120d91b4 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -1,5 +1,5 @@ +import { isPlainObject } from '@stoplight/json'; import { - DeepPartial, Dictionary, HttpParamStyles, IHttpEncoding, @@ -8,48 +8,69 @@ import { INodeExample, Optional, } from '@stoplight/types'; -import { JSONSchema7 } from 'json-schema'; -import { compact, each, get, isObject, keys, map, omit, pickBy, union, values } from 'lodash'; -import { EncodingPropertyObject, HeaderObject, MediaTypeObject, OpenAPIObject } from 'openapi3-ts'; +import type { JSONSchema7 } from 'json-schema'; +import pickBy = require('lodash.pickby'); +import { isBoolean, isNonNullable, isString } from '../../guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import { ArrayCallbackParameters, Fragment } from '../../types'; +import { entries, maybeResolveLocalRef } from '../../utils'; import { isHeaderObject } from '../guards'; +import { Oas3TranslateFunction } from '../types'; + +const ACCEPTABLE_STYLES: (string | undefined)[] = [ + HttpParamStyles.Form, + HttpParamStyles.SpaceDelimited, + HttpParamStyles.PipeDelimited, + HttpParamStyles.DeepObject, +]; + +function isAcceptableStyle( + encodingPropertyObject: T, +): encodingPropertyObject is T & { + style: + | HttpParamStyles.Form + | HttpParamStyles.SpaceDelimited + | HttpParamStyles.PipeDelimited + | HttpParamStyles.DeepObject; +} { + return typeof encodingPropertyObject.style === 'string' && ACCEPTABLE_STYLES.includes(encodingPropertyObject.style); +} -function translateEncodingPropertyObject( - encodingPropertyObject: EncodingPropertyObject, - property: string, -): IHttpEncoding { - const acceptableStyles: (string | undefined)[] = [ - HttpParamStyles.Form, - HttpParamStyles.SpaceDelimited, - HttpParamStyles.PipeDelimited, - HttpParamStyles.DeepObject, - ]; - - if (encodingPropertyObject.style && !acceptableStyles.includes(encodingPropertyObject.style)) { - throw new Error( - `Encoding property style: '${encodingPropertyObject.style}' is incorrect, must be one of: ${acceptableStyles}`, - ); - } +const translateEncodingPropertyObject: Oas3TranslateFunction< + ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, + Optional +> = function ([property, encodingPropertyObject]) { + if (!isPlainObject(encodingPropertyObject)) return; + if (!isAcceptableStyle(encodingPropertyObject)) return; return { property, - ...encodingPropertyObject, - // workaround for 'style' being one of the accepted HttpParamStyles - style: encodingPropertyObject.style as any, - mediaType: encodingPropertyObject.contentType, - headers: compact( - map & unknown, Optional>( - encodingPropertyObject.headers, - translateHeaderObject, - ), + style: encodingPropertyObject.style, + headers: entries(encodingPropertyObject.headers).map(translateHeaderObject, this).filter(isNonNullable), + + ...pickBy( + { + allowReserved: encodingPropertyObject.allowReserved, + explode: encodingPropertyObject.explode, + }, + isBoolean, + ), + + ...pickBy( + { + mediaType: encodingPropertyObject.contentType, + }, + isString, ), }; -} +}; -export function translateHeaderObject(headerObject: unknown, name: string): Optional { - if (!isObject(headerObject)) return; +export const translateHeaderObject = < + Oas3TranslateFunction, Optional> +>function ([name, unresolvedHeaderObject]) { + const headerObject = this.maybeResolveLocalRef(unresolvedHeaderObject); + if (!isPlainObject(headerObject)) return; if (!isHeaderObject(headerObject)) { return { @@ -62,116 +83,150 @@ export function translateHeaderObject(headerObject: unknown, name: string): Opti const { content: contentObject } = headerObject; - const contentValue = values(contentObject)[0]; + const contentValue = isPlainObject(contentObject) ? Object.values(contentObject)[0] : null; - const baseContent = { - // TODO(SL-249): we are missing examples in our types, on purpose? - // examples: parameterObject.examples, - ...omit(headerObject, 'content', 'style', 'examples', 'example', 'schema'), + const baseContent: IHttpHeaderParam = { name, - style: headerObject?.style ?? HttpParamStyles.Simple, + style: HttpParamStyles.Simple, + + ...pickBy( + { + schema: isPlainObject(headerObject.schema) ? translateSchemaObject.call(this, headerObject.schema) : null, + content: headerObject.content, + }, + isNonNullable, + ), + + ...pickBy( + { + description: headerObject.description, + }, + isString, + ), + + ...pickBy( + { + allowEmptyValue: headerObject.allowEmptyValue, + allowReserved: headerObject.allowReserved, + + explode: headerObject.explode, + required: headerObject.required, + deprecated: headerObject.deprecated, + }, + isBoolean, + ), }; const examples: INodeExample[] = []; const encodings: IHttpEncoding[] = []; - if (contentValue) { - examples.push(...keys(contentValue.examples).map(transformExamples(contentValue))); + if (isPlainObject(contentValue)) { + examples.push(...entries(contentValue.examples).map(translateToExample, this).filter(isNonNullable)); - encodings.push(...values(contentValue.encoding)); + if (isPlainObject(contentValue.encoding)) { + encodings.push(...(Object.values(contentValue.encoding) as IHttpEncoding[])); + } - if (contentValue.example) { - examples.push({ - key: '__default_content', - value: contentValue.example, - }); + if ('example' in contentValue) { + examples.push(transformDefaultExample.call(this, '__default_content', contentValue.example)); } } - examples.push(...keys(headerObject.examples).map(transformExamples(headerObject))); + examples.push(...entries(headerObject.examples).map(translateToExample, this).filter(isNonNullable)); - if (headerObject.example) { - examples.push({ - key: '__default', - value: headerObject.example, - }); + if ('example' in headerObject) { + examples.push(transformDefaultExample.call(this, '__default', headerObject.example)); } - return pickBy({ + return { ...baseContent, - schema: get(headerObject, 'schema') as any, encodings, examples, - }) as unknown as IHttpHeaderParam; -} + }; +}; -export function translateMediaTypeObject( - document: DeepPartial, - mediaObject: unknown, - mediaType: string, -): Optional { - if (!isDictionary(mediaObject)) return; +const translateSchemaMediaTypeObject: Oas3TranslateFunction<[schema: unknown], Optional> = function ( + schema, +) { + if (!isPlainObject(schema)) return; - const resolvedMediaObject = resolveMediaObject(document, mediaObject); - const { schema, encoding, examples } = resolvedMediaObject; + return translateSchemaObject.call(this, schema); +}; - let jsonSchema: Optional; +export const translateMediaTypeObject: Oas3TranslateFunction< + ArrayCallbackParameters<[mediaType: string, mediaObject: unknown]>, + Optional +> = function ([mediaType, mediaObject]) { + if (!isPlainObject(mediaObject)) return; - if (isObject(schema)) { - try { - jsonSchema = translateSchemaObject(document, schema); - } catch { - // happens - } - } + const resolvedMediaObject = resolveMediaObject(this.document, mediaObject); + const { schema, encoding, examples } = resolvedMediaObject; + + const jsonSchema = translateSchemaMediaTypeObject.call(this, schema); const example = resolvedMediaObject.example || jsonSchema?.examples?.[0]; return { mediaType, - schema: jsonSchema, // Note that I'm assuming all references are resolved - examples: compact( - union( - example ? [{ key: 'default', value: example }] : undefined, - isDictionary(examples) - ? Object.keys(examples).map(exampleKey => ({ - key: exampleKey, - summary: get(examples, [exampleKey, 'summary']), - description: get(examples, [exampleKey, 'description']), - value: get(examples, [exampleKey, 'value']), - })) - : [], - ), + examples: [ + example ? transformDefaultExample.call(this, 'default', example) : undefined, + ...entries(examples).map(translateToExample, this), + ].filter(isNonNullable), + encodings: entries(encoding).map(translateEncodingPropertyObject, this).filter(isNonNullable), + + ...pickBy( + { + schema: jsonSchema, + }, + isNonNullable, ), - encodings: map(encoding, translateEncodingPropertyObject), }; -} +}; -function resolveMediaObject(document: DeepPartial, maybeMediaObject: Dictionary) { +function resolveMediaObject(document: unknown, maybeMediaObject: Dictionary) { const mediaObject = { ...maybeMediaObject }; - if (isDictionary(mediaObject.schema)) { + if (isPlainObject(mediaObject.schema)) { mediaObject.schema = maybeResolveLocalRef(document, mediaObject.schema); } - if (isDictionary(mediaObject.examples)) { + if (isPlainObject(mediaObject.examples)) { const examples = { ...mediaObject.examples }; mediaObject.examples = examples; - each(examples, (exampleValue, exampleName) => { + for (const [exampleName, exampleValue] of entries(examples)) { examples[exampleName] = maybeResolveLocalRef(document, exampleValue); - }); + } } return mediaObject; } -const transformExamples = - (source: MediaTypeObject | HeaderObject) => - (key: string): INodeExample => { - return { - summary: get(source, ['examples', key, 'summary']), - description: get(source, ['examples', key, 'description']), - value: get(source, ['examples', key, 'value']), - key, - }; +const transformDefaultExample: Oas3TranslateFunction<[key: string, value: unknown], INodeExample> = function ( + key, + value, +) { + return { + value, + key, + }; +}; + +const translateToExample: Oas3TranslateFunction< + ArrayCallbackParameters<[key: string, example: unknown]>, + Optional +> = function ([key, example]) { + if (!isPlainObject(example)) return; + + return { + value: example.value, + key, + + ...pickBy( + { + summary: example.summary, + description: example.description, + }, + isString, + ), }; +}; diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index b0e99d27..fd68d1ef 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -1,107 +1,149 @@ +import { isPlainObject } from '@stoplight/json'; import type { - DeepPartial, - Dictionary, - IHttpCookieParam, IHttpHeaderParam, IHttpOperationRequest, IHttpOperationRequestBody, IHttpParam, - IHttpPathParam, - IHttpQueryParam, - IMediaTypeContent, + INodeExample, + INodeExternalExample, Optional, } from '@stoplight/types'; -import { compact, map, omit, partial, pickBy } from 'lodash'; -import type { OpenAPIObject, ParameterObject, RequestBodyObject } from 'openapi3-ts'; +import { HttpParamStyles } from '@stoplight/types'; +import type { JSONSchema7 } from 'json-schema'; +import type { ParameterObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import { isBoolean, isNonNullable, isString } from '../../guards'; +import { OasVersion } from '../../oas'; +import { getValidOasParameters } from '../../oas/accessors'; +import { isValidParamStyle } from '../../oas/guards'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import type { ArrayCallbackParameters, Fragment } from '../../types'; +import { entries } from '../../utils'; import { isRequestBodyObject } from '../guards'; +import type { Oas3TranslateFunction } from '../types'; import { translateMediaTypeObject } from './content'; -function translateRequestBody( - document: DeepPartial, - requestBodyObject: RequestBodyObject, -): IHttpOperationRequestBody { +export const translateRequestBody: Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> = + function (requestBodyObject) { + const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); + + if (isRequestBodyObject(resolvedRequestBodyObject)) { + return { + contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), + + ...pickBy( + { + required: resolvedRequestBodyObject.required, + description: resolvedRequestBodyObject.description, + }, + isNonNullable, + ), + }; + } + + return { contents: [] }; + }; + +const translateParameterObjectSchema: Oas3TranslateFunction< + [parameterObject: Fragment], + Optional +> = function (parameterObject) { + if (!isPlainObject(parameterObject.schema)) return; + + return translateSchemaObject.call(this, { + ...parameterObject.schema, + ...('example' in parameterObject ? { example: parameterObject.example } : null), + }); +}; + +const translateToExample: Oas3TranslateFunction< + ArrayCallbackParameters<[key: string, example: unknown]>, + Optional +> = function ([key, example]) { + if (!isPlainObject(example)) return; + + if (!('value' in example) && typeof example.externalValue !== 'string') return; + return { - required: requestBodyObject.required, - description: requestBodyObject.description, - contents: compact( - map & unknown, Optional>( - requestBodyObject.content, - partial(translateMediaTypeObject, document), - ), + key, + + ...(typeof example.externalValue === 'string' + ? { externalValue: example.externalValue } + : { value: example.value }), + + ...pickBy( + { + summary: example.summary, + description: example.description, + }, + isString, ), }; -} +}; -export function translateParameterObject( - document: DeepPartial, - parameterObject: ParameterObject, -): IHttpParam | any { - const examples = map(parameterObject.examples, (example, key) => ({ - key, - ...example, - })); - - const hasDefaultExample = examples.map(({ key }) => key).includes('default'); - - return pickBy({ - ...omit(parameterObject, 'in', 'schema', 'example'), - name: parameterObject.name, - style: parameterObject.style, - schema: isDictionary(parameterObject.schema) - ? translateSchemaObject(document, { - ...parameterObject.schema, - ...('example' in parameterObject ? { example: parameterObject.example } : null), - }) - : void 0, - examples: - 'example' in parameterObject && !hasDefaultExample - ? [{ key: 'default', value: parameterObject.example }, ...examples] - : examples, - }); -} - -export function translateToRequest( - document: DeepPartial, - parameters: ParameterObject[], - requestBodyObject?: RequestBodyObject, -): IHttpOperationRequest { - const params: { - header: IHttpHeaderParam[]; - query: IHttpQueryParam[]; - cookie: IHttpCookieParam[]; - path: IHttpPathParam[]; - } = { - header: [], - query: [], - cookie: [], - path: [], +export const translateParameterObject: Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> = + function (parameterObject) { + const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); + + const hasDefaultExample = examples.some(({ key }) => key.includes('default')); + const schema = translateParameterObjectSchema.call(this, parameterObject); + + return { + name: parameterObject.name, + style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, + examples: + 'example' in parameterObject && !hasDefaultExample + ? [{ key: 'default', value: parameterObject.example }, ...examples] + : examples, + + ...pickBy( + { + description: parameterObject.description, + }, + isString, + ), + + ...pickBy( + { + explode: parameterObject.explode, + deprecated: parameterObject.deprecated, + }, + isBoolean, + ), + + ...pickBy( + { + schema, + content: parameterObject.content, + }, + isPlainObject, + ), + }; }; - for (const parameter of parameters) { - const { in: key } = parameter; - if (!params.hasOwnProperty(key)) continue; +export const translateToRequest: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IHttpOperationRequest> = + function (path, operation) { + const params: Omit & { header: IHttpHeaderParam[] } = { + header: [], + query: [], + cookie: [], + path: [], + }; - params[key].push(translateParameterObject(document, parameter)); - } + for (const param of getValidOasParameters(this.document, OasVersion.OAS3, operation.parameters, path.parameters)) { + const { in: key } = param; + const target = params[key]; + if (!Array.isArray(target)) continue; - let body; - if (isDictionary(requestBodyObject)) { - const resolvedRequestBodyObject = maybeResolveLocalRef(document, requestBodyObject) as RequestBodyObject; - body = isRequestBodyObject(resolvedRequestBodyObject) - ? translateRequestBody(document, resolvedRequestBodyObject) - : { contents: [] }; - } else { - body = { contents: [] }; - } + target.push(translateParameterObject.call(this, param) as any); + } - return { - body, - headers: params.header, - query: params.query, - cookie: params.cookie, - path: params.path, + return { + body: translateRequestBody.call(this, operation?.requestBody), + headers: params.header, + query: params.query, + cookie: params.cookie, + path: params.path, + }; }; -} diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index 4a2e8742..2f7225f9 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,56 +1,36 @@ -import { - DeepPartial, - Dictionary, - IHttpHeaderParam, - IHttpOperationResponse, - IMediaTypeContent, - Optional, -} from '@stoplight/types'; -import { compact, map, partial, reduce } from 'lodash'; -import { OpenAPIObject } from 'openapi3-ts'; +import type { IHttpOperationResponse, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); -import { isDictionary, maybeResolveLocalRef } from '../../utils'; +import { isNonNullable, isString } from '../../guards'; +import type { ArrayCallbackParameters } from '../../types'; +import { entries } from '../../utils'; import { isResponseObject } from '../guards'; +import type { Oas3TranslateFunction } from '../types'; import { translateHeaderObject, translateMediaTypeObject } from './content'; -function translateToResponse( - document: DeepPartial, - response: unknown, - statusCode: string, -): Optional { - const resolvedResponse = maybeResolveLocalRef(document, response); +const translateToResponse: Oas3TranslateFunction< + ArrayCallbackParameters<[statusCode: string, response: unknown]>, + Optional +> = function ([statusCode, response]) { + const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; - const dereferencedHeaders = reduce( - resolvedResponse.headers, - (result, header, name) => { - return { ...result, [name]: maybeResolveLocalRef(document, header) }; - }, - {}, - ); - return { code: statusCode, - description: resolvedResponse.description, - headers: compact(map(dereferencedHeaders, translateHeaderObject)), - contents: compact( - map & unknown, Optional>( - resolvedResponse.content, - partial(translateMediaTypeObject, document), - ), + headers: entries(resolvedResponse.headers).map(translateHeaderObject, this).filter(isNonNullable), + contents: entries(resolvedResponse.content).map(translateMediaTypeObject, this).filter(isNonNullable), + + ...pickBy( + { + description: resolvedResponse.description, + }, + isString, ), }; -} - -export function translateToResponses( - document: DeepPartial, - responses: unknown, -): IHttpOperationResponse[] { - if (!isDictionary(responses)) { - return []; - } +}; - return compact( - map, Optional>(responses, partial(translateToResponse, document)), - ); -} +export const translateToResponses: Oas3TranslateFunction<[responses: unknown], IHttpOperationResponse[]> = function ( + responses, +) { + return entries(responses).map(translateToResponse, this).filter(isNonNullable); +}; diff --git a/src/oas3/transformers/securities.ts b/src/oas3/transformers/securities.ts index 2f5d45e2..53618e93 100644 --- a/src/oas3/transformers/securities.ts +++ b/src/oas3/transformers/securities.ts @@ -1,29 +1,26 @@ -import { DeepPartial, IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; -import { OpenAPIObject, SecuritySchemeObject } from 'openapi3-ts'; +import { isPlainObject } from '@stoplight/json'; +import type { IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; +import type { SecuritySchemeObject } from 'openapi3-ts'; -import { isDictionary } from '../../utils'; -import { getSecurities, OperationSecurities, SecurityWithKey } from '../accessors'; +import { isNonNullable } from '../../guards'; +import { ArrayCallbackParameters } from '../../types'; +import { getSecurities, SecurityWithKey } from '../accessors'; import { isOAuthFlowObject } from '../guards'; +import { Oas3TranslateFunction } from '../types'; -export function translateToSecurities(document: DeepPartial, operationSecurities: OperationSecurities) { - const securities = getSecurities(document, operationSecurities); +export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = + function (operationSecurities) { + const securities = getSecurities(this.document, operationSecurities); - return securities.map(security => - security.reduce((transformedSecurities, sec) => { - const transformed = transformToSingleSecurity(sec, sec.key); - if (transformed) { - transformedSecurities.push(transformed); - } + return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); + }; - return transformedSecurities; - }, []), - ); -} +export const translateToSingleSecurity: Oas3TranslateFunction< + [ArrayCallbackParameters & { type: 'mutualTLS' })>[0]], + Optional +> = function (securityScheme) { + const { key } = securityScheme; -export function transformToSingleSecurity( - securityScheme: SecuritySchemeObject | (Omit & { type: 'mutualTLS' }), - key: string, -): SecurityWithKey | undefined { const baseObject: { key: string; description?: string } = { key, }; @@ -82,12 +79,12 @@ export function transformToSingleSecurity( } return undefined; -} +}; function transformFlows(flows: Optional): IOauthFlowObjects { const transformedFlows: IOauthFlowObjects = {}; - if (!isDictionary(flows)) { + if (!isPlainObject(flows)) { return transformedFlows; } diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index ae0963c1..9a22e861 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,24 +1,89 @@ -import type { DeepPartial, Dictionary, INodeVariable, IServer } from '@stoplight/types'; -import { map, mapValues, pickBy } from 'lodash'; -import { ServerObject, ServerVariableObject } from 'openapi3-ts'; +import type { INodeVariable, IServer, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); -import { isServerVariableObject } from '../guards'; +import { isNonNullable, isString } from '../../guards'; +import { ArrayCallbackParameters, Fragment } from '../../types'; +import { entries } from '../../utils'; +import { isServerObject, isServerVariableObject } from '../guards'; +import { Oas3TranslateFunction } from '../types'; -export function translateToServers(servers: ServerObject[]): IServer[] { - return servers.map(server => ({ - description: server.description, +export const translateToServers: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IServer[]> = function ( + path, + operation, +) { + let servers; + if (Array.isArray(operation.servers)) { + servers = operation.servers; + } else if (Array.isArray(path.servers)) { + servers = path.servers; + } else if (Array.isArray(this.document.servers)) { + servers = this.document.servers; + } else { + return []; + } + + return servers.map(translateToServer, this).filter(isNonNullable); +}; + +export const translateToServer: Oas3TranslateFunction, Optional> = function ( + server, +) { + if (!isServerObject(server)) return; + + const variables = translateServerVariables.call(this, server.variables); + + return { url: server.url, - variables: server.variables && translateServerVariables(server.variables), - })); -} - -export function translateServerVariables(variables: DeepPartial<{ [v: string]: ServerVariableObject }>) { - return mapValues, INodeVariable>( - pickBy(variables, isServerVariableObject), - value => ({ - default: String(value.default), - description: value.description && String(value.description), - enum: value.enum && map(value.enum, String), - }), - ); -} + + ...pickBy( + { + name: this.document.info?.title, + description: server.description, + }, + isString, + ), + + ...pickBy( + { + variables, + }, + isNonNullable, + ), + }; +}; + +export const translateServerVariables: Oas3TranslateFunction< + [variables: unknown], + Optional> +> = variables => { + const serverVariables = entries(variables).map(translateServerVariable).filter(isNonNullable); + return serverVariables.length > 0 ? Object.fromEntries(serverVariables) : undefined; +}; + +const translateServerVariable: Oas3TranslateFunction< + ArrayCallbackParameters<[name: string, variable: unknown]>, + Optional<[string, INodeVariable]> +> = function ([name, variable]) { + if (!isServerVariableObject(variable)) return; + + return [ + name, + { + default: String(variable.default), + + ...pickBy( + { + description: variable.description, + }, + isString, + ), + + ...pickBy( + { + enum: Array.isArray(variable.enum) ? variable.enum.map(String) : undefined, + }, + isNonNullable, + ), + }, + ]; +}; diff --git a/src/oas3/types.ts b/src/oas3/types.ts new file mode 100644 index 00000000..c452b6b3 --- /dev/null +++ b/src/oas3/types.ts @@ -0,0 +1,10 @@ +import { DeepPartial } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; + +import { TranslateFunction } from '../types'; + +export type Oas3TranslateFunction

= TranslateFunction< + DeepPartial, + P, + R +>; diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index 3efb81d4..fd51a78c 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -255,7 +255,7 @@ describe('transformBody()', () => { ); // verify shape of example object - expect(Object.entries((result!.contents![0].examples![0] as INodeExample).value)).toEqual( + expect(Object.entries((result!.contents![0].examples![0] as INodeExample).value as unknown[])).toEqual( expect.arrayContaining([ [expect.stringMatching(/^_gen_[0-9a-f]{6}$/), 'v1'], [expect.stringMatching(/^_gen_[0-9a-f]{6}$/), 'v2'], @@ -264,7 +264,7 @@ describe('transformBody()', () => { // ensure both share exactly the same keys expect(Object.keys(result!.contents![0].schema!.properties!).sort()).toEqual( - Object.keys((result!.contents![0].examples![0] as INodeExample).value).sort(), + Object.keys((result!.contents![0].examples![0] as INodeExample).value as unknown[]).sort(), ); }); }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 8ab4f976..341e8fa4 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -6,13 +6,13 @@ import { IHttpQueryParam, IMediaTypeContent, } from '@stoplight/types'; -import { JSONSchema7 } from 'json-schema'; +import type { JSONSchema7 } from 'json-schema'; // @ts-ignore import * as jsonSchemaGenerator from 'json-schema-generator'; import { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; -import { translateSchemaObject } from '../../oas/transformers/schema'; +import { convertSchema } from '../../oas/transformers/schema'; import { transformDescriptionDefinition, transformStringValueToSchema } from '../util'; export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { @@ -82,7 +82,7 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): value: parsed, }, ], - schema: translateSchemaObject({}, jsonSchemaGenerator(parsed)), + schema: convertSchema(jsonSchemaGenerator(parsed)), }; } catch (e) { /* noop, move on.. */ @@ -104,7 +104,7 @@ function transformParamsBody( params: PropertyList, mediaType: string, ): IMediaTypeContent { - const paramsList: { name: string; schema: JSONSchema7; value: any }[] = params.map(item => { + const paramsList: { name: string; schema: JSONSchema7; value: unknown }[] = params.map(item => { return { name: item.key || generateId(), schema: { diff --git a/src/postman/transformers/securityScheme.ts b/src/postman/transformers/securityScheme.ts index ecc087e4..816b2cb3 100644 --- a/src/postman/transformers/securityScheme.ts +++ b/src/postman/transformers/securityScheme.ts @@ -1,6 +1,6 @@ import { HttpParamStyles, HttpSecurityScheme, IHttpHeaderParam, IHttpQueryParam } from '@stoplight/types'; -import { isEqual, omit } from 'lodash'; import { Collection, RequestAuth } from 'postman-collection'; +import isEqual = require('lodash.isequal'); export type PostmanSecurityScheme = StandardSecurityScheme | QuerySecurityScheme | HeaderSecurityScheme; @@ -227,7 +227,9 @@ export function isPostmanSecuritySchemeEqual(pss1: PostmanSecurityScheme, pss2: if (pss1.type !== pss2.type) return false; if (isStandardSecurityScheme(pss1) && isStandardSecurityScheme(pss2)) { - return isEqual(omit(pss1.securityScheme, 'key'), omit(pss2.securityScheme, 'key')); + const { key: _key, ...pss1SecurityScheme } = pss1.securityScheme; + const { key: _key2, ...pss2SecurityScheme } = pss2.securityScheme; + return isEqual(pss1SecurityScheme, pss2SecurityScheme); } return isEqual(pss1, pss2); diff --git a/src/types.ts b/src/types.ts index f4552e2a..5ec95eff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,15 +1,29 @@ -import { DeepPartial, IHttpOperation, IHttpService } from '@stoplight/types'; +import type { IHttpOperation, IHttpService } from '@stoplight/types'; -export interface ITransformServiceOpts { - document: DeepPartial; +export type Fragment = Record; + +export interface ITransformServiceOpts { + document: T; } export type HttpServiceTransformer = (opts: T) => IHttpService; -export interface ITransformOperationOpts { - document: DeepPartial; +export interface ITransformOperationOpts { + document: T; path: string; method: string; } export type HttpOperationTransformer = (opts: T) => IHttpOperation; + +export type TransformerContext = { + maybeResolveLocalRef(target: unknown): unknown; + document: T; +}; + +export type TranslateFunction = ( + this: TransformerContext, + ...params: P +) => R; + +export type ArrayCallbackParameters = [T, number, T[]]; diff --git a/src/utils.ts b/src/utils.ts index 4b0d5ea0..3d177946 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,7 @@ -import { hasRef, isLocalRef, resolveInlineRef } from '@stoplight/json'; -import { Dictionary, Optional } from '@stoplight/types'; -import { isObjectLike, map } from 'lodash'; - -export function mapToKeys(collection: Optional) { - return map(collection, Object.keys); -} - -export const isDictionary = (maybeDictionary: unknown): maybeDictionary is Dictionary => - isObjectLike(maybeDictionary); +import { hasRef, isLocalRef, isPlainObject, resolveInlineRef } from '@stoplight/json'; export const maybeResolveLocalRef = (document: unknown, target: unknown): unknown => { - if (isDictionary(document) && hasRef(target) && isLocalRef(target.$ref)) { + if (isPlainObject(document) && hasRef(target) && isLocalRef(target.$ref)) { try { return resolveInlineRef(document, target.$ref); } catch { @@ -20,3 +11,11 @@ export const maybeResolveLocalRef = (document: unknown, target: unknown): unknow return target; }; + +export { isPlainObject as isDictionary }; + +export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; +export function entries(o: T): [string, T][]; +export function entries(o: T): [string, T][] { + return isPlainObject(o) ? Object.entries(o as T) : []; +} diff --git a/yarn.lock b/yarn.lock index b0e76aba..88d5e7d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,7 +1081,7 @@ resolved "https://registry.yarnpkg.com/@stoplight/test-utils/-/test-utils-0.0.1.tgz#b0d38c8a0abebda2dacbc2aa6a4f9bec44878e7b" integrity sha512-Cj3waLFR9bYLG8yvgkjXMxOfiVdewRyrKdH5RQpttVNfKj5UF+mElofPcHn/UfiOuxK3t5rbsdMfS26LK5tdQg== -"@stoplight/types@^12.3.0", "@stoplight/types@^12.4.0": +"@stoplight/types@^12.3.0": version "12.4.0" resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-12.4.0.tgz#7a90e747d3f531742316b7b2969e6c45efd3e968" integrity sha512-ELf0dOdROaKEE0iw1YF9qbL9QjIVfdyiCoBPpUQmvQej9F7tQR/TsYMvyUb8kz+rd49u3hGP/sirVuaTKI1ZOg== @@ -1089,6 +1089,14 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" +"@stoplight/types@^13.0.0-beta.4": + version "13.0.0-beta.4" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.4.tgz#5ea5e93957b20a5effedcbd101e6e9cde7118e48" + integrity sha512-q9Dbyfi1DcZjTuDwptVLTCbeQgtpfTQAKQkbcrs64ayfvc7dPwO+81vYr0dSJsXzlFQktZv5M+eq0QPtJy/HGg== + dependencies: + "@types/json-schema" "^7.0.4" + utility-types "^3.10.0" + "@tootallnate/once@1": version "1.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" @@ -1192,10 +1200,31 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash@4.14.157": - version "4.14.157" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.157.tgz#fdac1c52448861dfde1a2e1515dbc46e54926dc8" - integrity sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ== +"@types/lodash.isequal@^4.5.5": + version "4.5.5" + resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz#4fed1b1b00bef79e305de0352d797e9bb816c8ff" + integrity sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.pick@^4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/lodash.pick/-/lodash.pick-4.4.6.tgz#ae4e8f109e982786313bb6aac4b1a73aefa6e9be" + integrity sha512-u8bzA16qQ+8dY280z3aK7PoWb3fzX5ATJ0rJB6F+uqchOX2VYF02Aqa+8aYiHiHgPzQiITqCgeimlyKFy4OA6g== + dependencies: + "@types/lodash" "*" + +"@types/lodash.pickby@^4.6.6": + version "4.6.6" + resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz#3dc39c2b38432f7a0c5e5627b0d5c0e3878b4f35" + integrity sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.180" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" + integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== "@types/node@*", "@types/node@>= 8": version "13.9.1" @@ -1253,11 +1282,6 @@ dependencies: "@types/node" "*" -"@types/urijs@~1.19.9": - version "1.19.17" - resolved "https://registry.yarnpkg.com/@types/urijs/-/urijs-1.19.17.tgz#232ac9884b6a2aeab5dbe70b79cdb91d5067c325" - integrity sha512-ShIlp+8iNGo/yVVfYFoNRqUiaE9wMCzsSl85qTg2/C5l56BTJokU7QeMgVBQ9xhcyhWQP0zGXPBZPPvEG/sRmQ== - "@types/yargs-parser@*": version "13.1.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" @@ -5358,11 +5382,6 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-schema@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -5807,6 +5826,11 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -5837,6 +5861,16 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.pickby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" + integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8= + lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" From c10468a40e86edd8a257cf30db8b3bd2442cc212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Wed, 30 Mar 2022 07:14:21 +0200 Subject: [PATCH 10/16] feat: generate stable ids (#177) --- jest.config.js | 1 + package.json | 9 +- src/__tests__/merge.test.ts | 244 ++++++--- src/__tests__/utils.test.ts | 37 -- src/context.ts | 66 ++- src/merge.ts | 5 +- .../__snapshots__/operation.test.ts.snap | 313 +++++++++++- .../__snapshots__/service.test.ts.snap | 18 +- src/oas/__tests__/accessors.test.ts | 182 ++++--- .../__tests__/fixtures/oas2-kitchen-sink.json | 1 + .../__tests__/fixtures/oas3-kitchen-sink.json | 1 + src/oas/__tests__/service.test.ts | 2 +- src/oas/__tests__/tags.test.ts | 13 +- src/oas/accessors.ts | 68 ++- src/oas/operation.ts | 61 ++- src/oas/resolver.ts | 31 ++ src/oas/service.ts | 5 +- src/oas/tags.ts | 22 +- src/oas/transformers/examples.ts | 18 + .../schema/__tests__/schema.spec.ts | 16 +- src/oas/transformers/schema/index.ts | 40 +- .../__snapshots__/operation.test.ts.snap | 9 +- src/oas2/__tests__/guards.test.ts | 39 -- src/oas2/__tests__/operation.test.ts | 45 +- src/oas2/__tests__/service.test.ts | 27 +- src/oas2/accessors.ts | 8 +- src/oas2/guards.ts | 8 + src/oas2/index.ts | 1 - src/oas2/operation.ts | 56 +-- src/oas2/service.ts | 5 +- .../__snapshots__/params.test.ts.snap | 18 + .../__snapshots__/responses.test.ts.snap | 228 ++++----- .../transformers/__tests__/params.test.ts | 81 ++- .../transformers/__tests__/request.test.ts | 5 +- .../transformers/__tests__/responses.test.ts | 151 ++++-- .../transformers/__tests__/securities.test.ts | 19 +- .../transformers/__tests__/servers.test.ts | 15 +- src/oas2/transformers/params.ts | 137 +++-- src/oas2/transformers/request.ts | 38 +- src/oas2/transformers/responses.ts | 51 +- src/oas2/transformers/securities.ts | 63 ++- src/oas2/transformers/servers.ts | 46 +- src/oas3/__fixtures__/id.json | 214 ++++++++ src/oas3/__fixtures__/output.ts | 471 ++++++++++++++++++ .../__snapshots__/operation.test.ts.snap | 27 +- src/oas3/__tests__/accessors.test.ts | 96 +--- src/oas3/__tests__/ids.test.ts | 12 + src/oas3/__tests__/operation.test.ts | 185 +++++-- src/oas3/__tests__/service.test.ts | 39 +- src/oas3/accessors.ts | 46 +- src/oas3/guards.ts | 5 - src/oas3/index.ts | 1 - src/oas3/operation.ts | 54 +- src/oas3/service.ts | 28 +- .../__snapshots__/content.test.ts.snap | 23 + .../__snapshots__/request.test.ts.snap | 7 + .../__snapshots__/responses.test.ts.snap | 161 +++--- .../__snapshots__/servers.test.ts.snap | 30 -- .../transformers/__tests__/content.test.ts | 120 ++++- .../transformers/__tests__/request.test.ts | 42 +- .../transformers/__tests__/responses.test.ts | 135 +++-- .../transformers/__tests__/securities.test.ts | 21 +- .../transformers/__tests__/servers.test.ts | 33 +- src/oas3/transformers/content.ts | 115 ++--- src/oas3/transformers/examples.ts | 33 ++ src/oas3/transformers/request.ts | 202 ++++---- src/oas3/transformers/responses.ts | 21 +- src/oas3/transformers/securities.ts | 27 +- src/oas3/transformers/servers.ts | 23 +- src/postman/__tests__/operation.test.ts | 36 +- src/postman/__tests__/service.test.ts | 1 + src/postman/__tests__/util.test.ts | 2 +- src/postman/id.ts | 8 + src/postman/operation.ts | 6 +- src/postman/service.ts | 4 +- .../transformers/__tests__/params.test.ts | 63 ++- .../transformers/__tests__/request.test.ts | 12 +- .../transformers/__tests__/response.test.ts | 24 + .../__tests__/securitySchemes.spec.ts | 79 ++- .../transformers/__tests__/server.test.ts | 10 +- src/postman/transformers/params.ts | 25 +- src/postman/transformers/response.ts | 4 + src/postman/transformers/securityScheme.ts | 43 +- src/postman/transformers/server.ts | 8 +- src/postman/types.ts | 4 +- src/postman/util.ts | 5 +- src/types.ts | 19 +- src/utils.ts | 24 +- yarn.lock | 201 ++++---- 89 files changed, 3497 insertions(+), 1455 deletions(-) delete mode 100644 src/__tests__/utils.test.ts create mode 100644 src/oas/resolver.ts create mode 100644 src/oas/transformers/examples.ts delete mode 100644 src/oas2/__tests__/guards.test.ts create mode 100644 src/oas3/__fixtures__/id.json create mode 100644 src/oas3/__fixtures__/output.ts create mode 100644 src/oas3/__tests__/ids.test.ts delete mode 100644 src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap create mode 100644 src/oas3/transformers/examples.ts create mode 100644 src/postman/id.ts diff --git a/jest.config.js b/jest.config.js index 4a5b465e..a9426838 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,5 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', + testMatch: ['/src/**/__tests__/*.(spec|test).(ts|js)'], }; diff --git a/package.json b/package.json index 6d059d7e..a1329423 100644 --- a/package.json +++ b/package.json @@ -52,13 +52,14 @@ "test": "jest" }, "dependencies": { - "@stoplight/json": "^3.17.2", - "@stoplight/types": "^13.0.0-beta.4", + "@stoplight/json": "^3.18.0", + "@stoplight/types": "^13.0.0-beta.5", "@types/json-schema": "7.0.5", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", + "fnv-plus": "^1.3.1", "json-schema-generator": "^2.0.6", - "lodash.isequal": "^4.5.0", + "lodash.isequalwith": "^4.4.0", "lodash.pick": "^4.4.0", "lodash.pickby": "^4.6.0", "openapi3-ts": "^2.0.1", @@ -71,7 +72,7 @@ "@stoplight/scripts": "^9.2.0", "@stoplight/test-utils": "^0.0.1", "@types/jest": "26.0.3", - "@types/lodash.isequal": "^4.5.5", + "@types/lodash.isequalwith": "^4.4.6", "@types/lodash.pick": "^4.4.6", "@types/lodash.pickby": "^4.6.6", "@types/postman-collection": "^3.5.3", diff --git a/src/__tests__/merge.test.ts b/src/__tests__/merge.test.ts index 2c21375c..031823ca 100644 --- a/src/__tests__/merge.test.ts +++ b/src/__tests__/merge.test.ts @@ -8,59 +8,66 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', headers: [ - { name: '200h1', style: HttpParamStyles.Simple, required: true }, - { name: '200h2', style: HttpParamStyles.Simple, required: true }, + { id: 'b', name: '200h1', style: HttpParamStyles.Simple, required: true }, + { id: 'c', name: '200h2', style: HttpParamStyles.Simple, required: true }, ], }, { + id: 'd', code: '400', headers: [ - { name: '400h1', style: HttpParamStyles.Simple, required: true }, - { name: '400h2', style: HttpParamStyles.Simple, required: true }, + { id: 'e', name: '400h1', style: HttpParamStyles.Simple, required: true }, + { id: 'f', name: '400h2', style: HttpParamStyles.Simple, required: true }, ], }, ], [ { + id: 'g', code: '200', headers: [ - { name: '200h2', style: HttpParamStyles.Simple, required: true }, - { name: '200h3', style: HttpParamStyles.Simple, required: true }, + { id: 'h', name: '200h2', style: HttpParamStyles.Simple, required: true }, + { id: 'i', name: '200h3', style: HttpParamStyles.Simple, required: true }, ], }, { + id: 'j', code: '500', headers: [ - { name: '500h1', style: HttpParamStyles.Simple, required: true }, - { name: '500h2', style: HttpParamStyles.Simple, required: true }, + { id: 'k', name: '500h1', style: HttpParamStyles.Simple, required: true }, + { id: 'l', name: '500h2', style: HttpParamStyles.Simple, required: true }, ], }, ], ), ).toEqual([ { + id: 'a', code: '200', headers: [ - { name: '200h1', style: HttpParamStyles.Simple, required: false }, - { name: '200h2', style: HttpParamStyles.Simple, required: true }, - { name: '200h3', style: HttpParamStyles.Simple, required: false }, + { id: 'b', name: '200h1', style: HttpParamStyles.Simple, required: false }, + { id: 'c', name: '200h2', style: HttpParamStyles.Simple, required: true }, + { id: 'i', name: '200h3', style: HttpParamStyles.Simple, required: false }, ], contents: [], }, { + id: 'd', code: '400', headers: [ - { name: '400h1', style: HttpParamStyles.Simple, required: true }, - { name: '400h2', style: HttpParamStyles.Simple, required: true }, + { id: 'e', name: '400h1', style: HttpParamStyles.Simple, required: true }, + { id: 'f', name: '400h2', style: HttpParamStyles.Simple, required: true }, ], }, { + id: 'j', code: '500', headers: [ - { name: '500h1', style: HttpParamStyles.Simple, required: true }, - { name: '500h2', style: HttpParamStyles.Simple, required: true }, + { id: 'k', name: '500h1', style: HttpParamStyles.Simple, required: true }, + { id: 'l', name: '500h2', style: HttpParamStyles.Simple, required: true }, ], }, ]); @@ -71,45 +78,50 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, - { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'b', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'c', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'd', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, ], [ { + id: 'e', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, - { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'string' } }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'f', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'g', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'string' } }, + { id: 'h', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, { + id: 'i', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, - { name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'boolean' } }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'j', name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'k', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { type: 'boolean' } }, + { id: 'l', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], }, ], ), ).toEqual([ { + id: 'a', code: '200', headers: [ - { name: 'h1', style: HttpParamStyles.Simple, required: true }, + { id: 'b', name: 'h1', style: HttpParamStyles.Simple, required: true }, { + id: 'c', name: 'h2', style: HttpParamStyles.Simple, required: true, schema: { anyOf: [{ type: 'number' }, { type: 'string' }, { type: 'boolean' }] }, }, - { name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, + { id: 'd', name: 'h3', style: HttpParamStyles.Simple, required: true, schema: { type: 'number' } }, ], contents: [], }, @@ -121,21 +133,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - headers: [{ name: 'h', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'b', name: 'h', style: HttpParamStyles.Simple, required: true }], }, ], [ { + id: 'c', code: '200', - headers: [{ name: 'h', style: HttpParamStyles.Simple, required: false }], + headers: [{ id: 'd', name: 'h', style: HttpParamStyles.Simple, required: false }], }, ], ), ).toEqual([ { + id: 'a', code: '200', - headers: [{ name: 'h', style: HttpParamStyles.Simple, required: false }], + headers: [{ id: 'b', name: 'h', style: HttpParamStyles.Simple, required: false }], contents: [], }, ]); @@ -146,21 +161,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - headers: [{ name: 'Oo', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'b', name: 'Oo', style: HttpParamStyles.Simple, required: true }], }, ], [ { + id: 'c', code: '200', - headers: [{ name: 'oO', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'd', name: 'oO', style: HttpParamStyles.Simple, required: true }], }, ], ), ).toEqual([ { + id: 'a', code: '200', - headers: [{ name: 'Oo', style: HttpParamStyles.Simple, required: true }], + headers: [{ id: 'b', name: 'Oo', style: HttpParamStyles.Simple, required: true }], contents: [], }, ]); @@ -171,38 +189,67 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - contents: [{ mediaType: '200-tion/a-son' }, { mediaType: '200-tion/b-son' }], + contents: [ + { id: 'a1', mediaType: '200-tion/a-son' }, + { id: 'a2', mediaType: '200-tion/b-son' }, + ], }, { + id: 'b', code: '400', - contents: [{ mediaType: '400-tion/a-son' }, { mediaType: '400-tion/b-son' }], + contents: [ + { id: 'b1', mediaType: '400-tion/a-son' }, + { id: 'b2', mediaType: '400-tion/b-son' }, + ], }, ], [ { + id: 'c', code: '200', - contents: [{ mediaType: '200-tion/b-son' }, { mediaType: '200-tion/c-son' }], + contents: [ + { id: 'c1', mediaType: '200-tion/b-son' }, + { id: 'c2', mediaType: '200-tion/c-son' }, + ], }, { + id: 'd', code: '500', - contents: [{ mediaType: '500-tion/a-son' }, { mediaType: '500-tion/b-son' }], + contents: [ + { id: 'd1', mediaType: '500-tion/a-son' }, + { id: 'd2', mediaType: '500-tion/b-son' }, + ], }, ], ), ).toEqual([ { + id: 'a', code: '200', - contents: [{ mediaType: '200-tion/a-son' }, { mediaType: '200-tion/b-son' }, { mediaType: '200-tion/c-son' }], + contents: [ + { id: 'a1', mediaType: '200-tion/a-son' }, + { id: 'a2', mediaType: '200-tion/b-son' }, + { id: 'c2', mediaType: '200-tion/c-son' }, + ], headers: [], }, { + id: 'b', code: '400', - contents: [{ mediaType: '400-tion/a-son' }, { mediaType: '400-tion/b-son' }], + contents: [ + { id: 'b1', mediaType: '400-tion/a-son' }, + { id: 'b2', mediaType: '400-tion/b-son' }, + ], }, { + id: 'd', code: '500', - contents: [{ mediaType: '500-tion/a-son' }, { mediaType: '500-tion/b-son' }], + contents: [ + { id: 'd1', mediaType: '500-tion/a-son' }, + { id: 'd2', mediaType: '500-tion/b-son' }, + ], }, ]); }); @@ -212,17 +259,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', contents: [ - { mediaType: 'application/json', schema: { type: 'object', properties: { a: { type: 'string' } } } }, + { + id: 'a1', + mediaType: 'application/json', + schema: { type: 'object', properties: { a: { type: 'string' } } }, + }, ], }, ], [ { + id: 'b', code: '200', contents: [ { + id: 'b1', mediaType: 'application/json', schema: { type: 'object', properties: { a: { type: 'string' }, b: { type: 'string' } } }, }, @@ -232,9 +286,11 @@ describe('mergeResponses()', () => { ), ).toEqual([ { + id: 'a', code: '200', contents: [ { + id: 'a1', mediaType: 'application/json', schema: { anyOf: [ @@ -254,21 +310,24 @@ describe('mergeResponses()', () => { mergeResponses( [ { + id: 'a', code: '200', - contents: [{ mediaType: 'Aa/Oo' }], + contents: [{ id: 'a1', mediaType: 'Aa/Oo' }], }, ], [ { + id: 'b', code: '200', - contents: [{ mediaType: 'aA/oO' }], + contents: [{ id: 'b1', mediaType: 'aA/oO' }], }, ], ), ).toEqual([ { + id: 'a', code: '200', - contents: [{ mediaType: 'Aa/Oo' }], + contents: [{ id: 'a1', mediaType: 'Aa/Oo' }], headers: [], }, ]); @@ -280,12 +339,28 @@ describe('mergeOperations()', () => { expect( mergeOperations( [ - { id: '1', method: 'get', path: '/a', responses: [{ code: '200' }, { code: '400' }] }, - { id: '2', method: 'get', path: '/b', responses: [{ code: '200' }] }, + { + id: '1', + method: 'get', + path: '/a', + responses: [ + { id: 'a', code: '200' }, + { id: 'b', code: '400' }, + ], + }, + { id: '2', method: 'get', path: '/b', responses: [{ id: 'c', code: '200' }] }, ], [ - { id: '3', method: 'get', path: '/a', responses: [{ code: '200' }, { code: '500' }] }, - { id: '4', method: 'get', path: '/c', responses: [{ code: '200' }] }, + { + id: '3', + method: 'get', + path: '/a', + responses: [ + { id: 'd', code: '200' }, + { id: 'e', code: '500' }, + ], + }, + { id: '4', method: 'get', path: '/c', responses: [{ id: 'f', code: '200' }] }, ], ), ).toEqual([ @@ -293,12 +368,16 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200', contents: [], headers: [] }, { code: '400' }, { code: '500' }], + responses: [ + { id: 'a', code: '200', contents: [], headers: [] }, + { id: 'b', code: '400' }, + { id: 'e', code: '500' }, + ], servers: [], request: {}, }, - { id: '2', method: 'get', path: '/b', responses: [{ code: '200' }] }, - { id: '4', method: 'get', path: '/c', responses: [{ code: '200' }] }, + { id: '2', method: 'get', path: '/b', responses: [{ id: 'c', code: '200' }] }, + { id: '4', method: 'get', path: '/c', responses: [{ id: 'f', code: '200' }] }, ]); }); @@ -310,8 +389,8 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200' }], - servers: [{ url: 'http://example.com' }], + responses: [{ id: 'a', code: '200' }], + servers: [{ id: 'b', url: 'http://example.com' }], }, ], [ @@ -319,8 +398,8 @@ describe('mergeOperations()', () => { id: '2', method: 'get', path: '/a', - responses: [{ code: '200' }], - servers: [{ url: 'https://example.com' }], + responses: [{ id: 'c', code: '200' }], + servers: [{ id: 'd', url: 'https://example.com' }], }, ], ), @@ -329,8 +408,11 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200', headers: [], contents: [] }], - servers: [{ url: 'http://example.com' }, { url: 'https://example.com' }], + responses: [{ id: 'a', code: '200', headers: [], contents: [] }], + servers: [ + { id: 'b', url: 'http://example.com' }, + { id: 'd', url: 'https://example.com' }, + ], request: {}, }, ]); @@ -344,22 +426,24 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200' }], + responses: [{ id: 'a', code: '200' }], request: { headers: [ - { name: 'a', style: HttpParamStyles.Simple, required: true }, - { name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'b', name: 'a', style: HttpParamStyles.Simple, required: true }, + { id: 'c', name: 'b', style: HttpParamStyles.Simple, required: true }, ], query: [ - { name: 'a', style: HttpParamStyles.Form, required: true }, - { name: 'b', style: HttpParamStyles.Form, required: true }, + { id: 'd', name: 'a', style: HttpParamStyles.Form, required: true }, + { id: 'e', name: 'b', style: HttpParamStyles.Form, required: true }, ], - path: [{ name: 'a', style: HttpParamStyles.Simple, required: true }], + path: [{ id: 'f', name: 'a', style: HttpParamStyles.Simple, required: true }], body: { + id: 'g', description: 'The cadillac stood by the house', required: true, contents: [ { + id: 'g1', mediaType: 'application/json', schema: { type: 'string' }, }, @@ -373,25 +457,27 @@ describe('mergeOperations()', () => { id: '2', method: 'get', path: '/a', - responses: [{ code: '200' }], + responses: [{ id: '11', code: '200' }], request: { headers: [ - { name: 'b', style: HttpParamStyles.Simple, required: true }, - { name: 'c', style: HttpParamStyles.Simple, required: true }, + { id: 'b1', name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'c1', name: 'c', style: HttpParamStyles.Simple, required: true }, ], query: [ - { name: 'b', style: HttpParamStyles.Form, required: true }, - { name: 'c', style: HttpParamStyles.Form, required: true }, + { id: 'd1', name: 'b', style: HttpParamStyles.Form, required: true }, + { id: 'e1', name: 'c', style: HttpParamStyles.Form, required: true }, ], path: [ - { name: 'a', style: HttpParamStyles.Simple, required: true }, - { name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'f1', name: 'a', style: HttpParamStyles.Simple, required: true }, + { id: 'g1', name: 'b', style: HttpParamStyles.Simple, required: true }, ], body: { + id: 'h1', description: 'And the yanks they were within', required: true, contents: [ { + id: 'h2', mediaType: 'application/json', schema: { type: 'number' }, }, @@ -406,27 +492,29 @@ describe('mergeOperations()', () => { id: '1', method: 'get', path: '/a', - responses: [{ code: '200', headers: [], contents: [] }], + responses: [{ id: 'a', code: '200', headers: [], contents: [] }], request: { headers: [ - { name: 'a', style: HttpParamStyles.Simple, required: false }, - { name: 'b', style: HttpParamStyles.Simple, required: true }, - { name: 'c', style: HttpParamStyles.Simple, required: false }, + { id: 'b', name: 'a', style: HttpParamStyles.Simple, required: false }, + { id: 'c', name: 'b', style: HttpParamStyles.Simple, required: true }, + { id: 'c1', name: 'c', style: HttpParamStyles.Simple, required: false }, ], query: [ - { name: 'a', style: HttpParamStyles.Form, required: false }, - { name: 'b', style: HttpParamStyles.Form, required: true }, - { name: 'c', style: HttpParamStyles.Form, required: false }, + { id: 'd', name: 'a', style: HttpParamStyles.Form, required: false }, + { id: 'e', name: 'b', style: HttpParamStyles.Form, required: true }, + { id: 'e1', name: 'c', style: HttpParamStyles.Form, required: false }, ], path: [ - { name: 'a', style: HttpParamStyles.Simple, required: true }, - { name: 'b', style: HttpParamStyles.Simple, required: false }, + { id: 'f', name: 'a', style: HttpParamStyles.Simple, required: true }, + { id: 'g1', name: 'b', style: HttpParamStyles.Simple, required: false }, ], body: { + id: 'g', required: true, description: 'The cadillac stood by the house; And the yanks they were within', contents: [ { + id: 'g1', mediaType: 'application/json', schema: { anyOf: [{ type: 'string' }, { type: 'number' }] }, }, diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts deleted file mode 100644 index 57cb1795..00000000 --- a/src/__tests__/utils.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { maybeResolveLocalRef } from '../utils'; - -describe('maybeResolveLocalRef()', () => { - it('follows $refs', () => { - expect( - maybeResolveLocalRef( - { - a: { - $ref: '#/b', - }, - b: { - c: 'woo', - }, - }, - { $ref: '#/a/c' }, - ), - ).toEqual('woo'); - }); - - it('handles invalid $refs', () => { - expect(maybeResolveLocalRef({ a: true }, { $ref: '#a' })).toStrictEqual({ $ref: '#a' }); - }); - - it('handles circular references', () => { - const document = { - get a(): unknown { - return this.a; - }, - }; - - expect( - maybeResolveLocalRef(document, { - $ref: '#/a', - }), - ).toStrictEqual({ $ref: '#/a' }); - }); -}); diff --git a/src/context.ts b/src/context.ts index d13668e0..0a4c3334 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,9 +1,67 @@ -import { TransformerContext } from './types'; -import { maybeResolveLocalRef } from './utils'; +import { hasRef, isLocalRef } from '@stoplight/json'; +// @ts-ignore +import * as fnv from 'fnv-plus'; -export function createContext>(document: T): TransformerContext { +import type { + AvailableContext, + Fragment, + IdGenerator, + RefResolver, + TransformerContext, + TranslateFunction, +} from './types'; + +export const DEFAULT_ID_GENERATOR: IdGenerator = input => fnv.fast1a52hex(input); + +export function createContext>( + document: T, + resolveRef: RefResolver, + generateId: IdGenerator, +): TransformerContext { + let context: AvailableContext = 'service'; return { document, - maybeResolveLocalRef: maybeResolveLocalRef.bind(null, document), + get context() { + return context; + }, + set context(value) { + context = value; + if (value !== 'operation') { + this.parentId = this.ids[value]; + } + }, + maybeResolveLocalRef(target: unknown) { + if (hasRef(target) && isLocalRef(target.$ref)) { + try { + return resolveRef.call(this, target); + } catch { + return target; + } + } + + return target; + }, + generateId(value) { + this.parentId = generateId(value); + return this.parentId; + }, + ids: { + service: '', + path: '', + operation: '', + }, + parentId: '', + }; +} + +export function withContext = TranslateFunction>( + fn: F, +): F { + return function (...args: Parameters) { + const { context, parentId } = this; + const result = fn.apply(this, args); + this.context = context; + this.parentId = parentId; + return result; }; } diff --git a/src/merge.ts b/src/merge.ts index b007adae..89f30131 100644 --- a/src/merge.ts +++ b/src/merge.ts @@ -8,7 +8,8 @@ import { IServer, } from '@stoplight/types'; import type { JSONSchema7 as JSONSchema } from 'json-schema'; -import isEqual = require('lodash.isequal'); + +import { isEqual } from './utils'; function isExclusivelyAnyOfSchema(schema: JSONSchema): schema is { anyOf: JSONSchema[] } { return !!(schema.anyOf && Object.keys(schema).length === 1); @@ -66,6 +67,7 @@ const mergeContents = mergeLists( (c1, c2) => c1.mediaType.toLowerCase() === c2.mediaType.toLowerCase(), (c1, c2) => { return { + id: c1.id, mediaType: c1.mediaType, schema: mergeTwo(mergeSchemas, c1.schema, c2.schema), examples: mergeContentExamples([c1.examples, c2.examples]), @@ -107,6 +109,7 @@ export const mergeResponses = mergeLists( function mergeRequestBodies(b1: IHttpOperationRequestBody, b2: IHttpOperationRequestBody): IHttpOperationRequestBody { return { + id: b1.id, description: [b1.description, b2.description].filter(Boolean).join('; ') || undefined, required: b1.required && b2.required, contents: mergeContents(b1.contents || [], b2.contents || []), diff --git a/src/oas/__tests__/__snapshots__/operation.test.ts.snap b/src/oas/__tests__/__snapshots__/operation.test.ts.snap index a4942a89..63e2e30f 100644 --- a/src/oas/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/operation.test.ts.snap @@ -10,7 +10,7 @@ Array [ }, "x-test": true, }, - "id": "?http-operation-id?", + "id": "9ab1c1339675f", "iid": "addPet", "method": "post", "path": "/pets", @@ -19,22 +19,113 @@ Array [ "contents": Array [ Object { "examples": Array [], + "id": "becbc0ed96d9d", "mediaType": "application/json", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, Object { "examples": Array [], + "id": "7e79049c5af23", "mediaType": "application/xml", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, ], "description": "Pet object that needs to be added to the store", + "id": "62e6080a56c45", "required": true, }, "cookie": Array [], @@ -48,6 +139,7 @@ Array [ "contents": Array [], "description": "Invalid input", "headers": Array [], + "id": "029110671da4a", }, ], "security": Array [ @@ -62,6 +154,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -69,10 +162,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -80,23 +175,26 @@ Array [ "summary": "Add a new pet to the store", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], }, Object { "extensions": Object {}, - "id": "?http-operation-id?", + "id": "29e0f9745e980", "method": "get", "path": "/pets", "request": Object { "cookie": Array [], "headers": Array [ Object { + "id": "b93f4ef60cf1d", "name": "Rate-Limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9360fa7dc92ab", }, "style": "simple", }, @@ -104,18 +202,22 @@ Array [ "path": Array [], "query": Array [ Object { + "id": "e1c08deab8b36", "name": "skip", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "828c85d6a2307", }, "style": "form", }, Object { + "id": "d04bcf1c141cf", "name": "limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "cdfdf74babd0b", }, "style": "form", }, @@ -127,15 +229,18 @@ Array [ "contents": Array [], "description": "", "headers": Array [], + "id": "6ae68ec4738b2", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -145,17 +250,19 @@ Array [ }, Object { "extensions": Object {}, - "id": "?http-operation-id?", + "id": "8bd67834e0236", "method": "options", "path": "/pets", "request": Object { "cookie": Array [], "headers": Array [ Object { + "id": "b93f4ef60cf1d", "name": "Rate-Limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9360fa7dc92ab", }, "style": "simple", }, @@ -163,18 +270,22 @@ Array [ "path": Array [], "query": Array [ Object { + "id": "e1c08deab8b36", "name": "skip", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "828c85d6a2307", }, "style": "form", }, Object { + "id": "d04bcf1c141cf", "name": "limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "cdfdf74babd0b", }, "style": "form", }, @@ -188,15 +299,18 @@ Array [ "headers": Array [ Object { "description": "Allowed clients", + "id": "0b9c41debc794", "name": "Allow", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "b5186fbb4fdf5", }, "style": "simple", }, Object { "description": "Remaining requests", + "id": "bbf28e682b54b", "name": "X-Rate-Limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -204,19 +318,23 @@ Array [ "maximum": 9223372036854776000, "minimum": -9223372036854776000, "type": "number", + "x-stoplight-id": "a07f1d06408fd", }, "style": "simple", }, ], + "id": "ee11fabc87ff7", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -227,7 +345,7 @@ Array [ Object { "description": "", "extensions": Object {}, - "id": "?http-operation-id?", + "id": "4f51735d06d77", "method": "delete", "path": "/pets", "request": Object { @@ -242,21 +360,25 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], + "id": "5dfa6d9892b22", }, Object { "code": "404", "contents": Array [], "description": "Pet not found", "headers": Array [], + "id": "5dba6d9892c7e", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -267,7 +389,7 @@ Array [ Object { "description": "", "extensions": Object {}, - "id": "?http-operation-id?", + "id": "fc497ea5adb47", "iid": "updatePet", "method": "put", "path": "/pet/{petId}", @@ -276,33 +398,126 @@ Array [ "contents": Array [ Object { "examples": Array [], + "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, Object { "examples": Array [], + "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { - "$ref": "#/definitions/Pet", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "category": Object { + "$ref": "#/definitions/Category", + }, + "id": Object { + "format": "int64", + "maximum": 9223372036854776000, + "minimum": -9223372036854776000, + "type": "integer", + }, + "name": Object { + "examples": Array [ + "doggie", + ], + "type": "string", + }, + "photoUrls": Object { + "items": Object { + "type": "string", + }, + "type": "array", + "xml": Object { + "name": "photoUrl", + "wrapped": true, + }, + }, + "status": Object { + "description": "pet status in the store", + "enum": Array [ + "available", + "pending", + "sold", + ], + "type": "string", + }, + }, + "required": Array [ + "name", + "photoUrls", + ], + "type": "object", + "x-stoplight-id": "2b4cfd51fbcde", + "xml": Object { + "name": "Pet", + }, }, }, ], "description": "Pet object that needs to be added to the store", + "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], "headers": Array [], "path": Array [ Object { + "id": "1ee79143656f4", "name": "petId", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "3c1327003ffa9", }, "style": "simple", }, @@ -315,35 +530,64 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], + "id": "c12ebf63598e3", }, Object { "code": "404", "contents": Array [ Object { "examples": Array [], + "id": "3d04477dddecb", "mediaType": "application/xml", "schema": Object { - "$ref": "#/definitions/Error", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "code": Object { + "type": "string", + }, + "message": Object { + "type": "string", + }, + }, + "required": Array [ + "code", + ], + "type": "object", + "x-stoplight-id": "b3c54fc227df9", }, }, Object { "examples": Array [], + "id": "d1be05066ac36", "mediaType": "application/json", "schema": Object { - "$ref": "#/definitions/Error", "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "code": Object { + "type": "string", + }, + "message": Object { + "type": "string", + }, + }, + "required": Array [ + "code", + ], + "type": "object", + "x-stoplight-id": "b3c54fc227df9", }, }, ], "description": "Our shared 404 response.", "headers": Array [], + "id": "e1deb92ee1e73", }, Object { "code": "405", "contents": Array [], "description": "Validation exception", "headers": Array [], + "id": "c15ebf6359d8c", }, ], "security": Array [ @@ -358,6 +602,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -365,10 +610,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -376,6 +623,7 @@ Array [ "summary": "Update an existing pet", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], @@ -393,7 +641,7 @@ Array [ }, "x-test": true, }, - "id": "?http-operation-id?", + "id": "9ab1c1339675f", "iid": "addPet", "method": "post", "path": "/pets", @@ -403,6 +651,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -447,6 +696,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -455,6 +705,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -499,6 +750,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -506,6 +758,7 @@ Array [ }, ], "description": "Pet object that needs to be added to the store", + "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -519,6 +772,7 @@ Array [ "contents": Array [], "description": "Invalid input", "headers": Array [], + "id": "029110671da4a", }, ], "security": Array [ @@ -533,6 +787,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -540,10 +795,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -551,18 +808,20 @@ Array [ "summary": "Add a new pet to the store", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], }, Object { "extensions": Object {}, - "id": "?http-operation-id?", + "id": "29e0f9745e980", "method": "get", "path": "/pets", "request": Object { "body": Object { "contents": Array [], + "id": "ccf0943cc3b92", }, "cookie": Array [], "headers": Array [], @@ -570,19 +829,23 @@ Array [ "query": Array [ Object { "examples": Array [], + "id": "e1c08deab8b36", "name": "skip", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "828c85d6a2307", }, "style": "simple", }, Object { "examples": Array [], + "id": "d04bcf1c141cf", "name": "limit", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "cdfdf74babd0b", }, "style": "simple", }, @@ -595,25 +858,30 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "61b0e53aa1cce", "mediaType": "*/*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "properties": Object {}, "type": "object", + "x-stoplight-id": "8ea4aec4c2854", }, }, ], "description": "OK", "headers": Array [], + "id": "6ae68ec4738b2", }, ], "security": Array [], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -624,7 +892,7 @@ Array [ Object { "description": "", "extensions": Object {}, - "id": "?http-operation-id?", + "id": "fc497ea5adb47", "iid": "updatePet", "method": "put", "path": "/pet/{petId}", @@ -634,6 +902,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "954d8d8652606", "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -678,6 +947,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -686,6 +956,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "1043a7cddde88", "mediaType": "application/xml", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -730,6 +1001,7 @@ Array [ "photoUrls", ], "type": "object", + "x-stoplight-id": "849b52cd05782", "xml": Object { "name": "Pet", }, @@ -737,6 +1009,7 @@ Array [ }, ], "description": "Pet object that needs to be added to the store", + "id": "9b5d65cfbc4fd", "required": true, }, "cookie": Array [], @@ -744,10 +1017,13 @@ Array [ "path": Array [ Object { "examples": Array [], + "id": "1ee79143656f4", "name": "petId", + "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "3c1327003ffa9", }, "style": "simple", }, @@ -760,6 +1036,7 @@ Array [ "contents": Array [], "description": "Invalid ID supplied", "headers": Array [], + "id": "c12ebf63598e3", }, Object { "code": "404", @@ -767,6 +1044,7 @@ Array [ Object { "encodings": Array [], "examples": Array [], + "id": "edbd8b027c519", "mediaType": "*/*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -782,17 +1060,20 @@ Array [ "code", ], "type": "object", + "x-stoplight-id": "b9501a9ded2c5", }, }, ], "description": "Our shared 404 response.", "headers": Array [], + "id": "e1deb92ee1e73", }, Object { "code": "405", "contents": Array [], "description": "Validation exception", "headers": Array [], + "id": "c15ebf6359d8c", }, ], "security": Array [ @@ -807,6 +1088,7 @@ Array [ }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, @@ -814,10 +1096,12 @@ Array [ ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -825,6 +1109,7 @@ Array [ "summary": "Update an existing pet", "tags": Array [ Object { + "id": "bfc2f4aa9bced", "name": "pet", }, ], diff --git a/src/oas/__tests__/__snapshots__/service.test.ts.snap b/src/oas/__tests__/__snapshots__/service.test.ts.snap index 2312c802..20fc18d1 100644 --- a/src/oas/__tests__/__snapshots__/service.test.ts.snap +++ b/src/oas/__tests__/__snapshots__/service.test.ts.snap @@ -6,7 +6,7 @@ Object { "email": "apiteam@swagger.io", }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key \`special-key\` to test the authorization filters.", - "id": "?http-service-id?", + "id": "abc", "license": Object { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", @@ -23,10 +23,12 @@ Object { }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, Object { + "id": "39070f74aaf58", "in": "header", "key": "api_key", "name": "api_key", @@ -35,10 +37,12 @@ Object { ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -46,14 +50,17 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", + "id": "bfc2f4aa9bced", "name": "pet", }, Object { "description": "Access to Petstore orders", + "id": "20c6cf86f6d6d", "name": "store", }, Object { "description": "Operations about user", + "id": "10e73f5f26c07", "name": "user", }, ], @@ -68,7 +75,7 @@ Object { "email": "apiteam@swagger.io", }, "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key \`special-key\` to test the authorization filters.", - "id": "?http-service-id?", + "id": "abc", "license": Object { "name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html", @@ -85,10 +92,12 @@ Object { }, }, }, + "id": "7d917a06422fe", "key": "petstore_auth", "type": "oauth2", }, Object { + "id": "39070f74aaf58", "in": "header", "key": "api_key", "name": "api_key", @@ -97,10 +106,12 @@ Object { ], "servers": Array [ Object { + "id": "be714555a4b1c", "name": "Swagger Petstore", "url": "https://petstore.swagger.io/v2", }, Object { + "id": "b9d12ab472c47", "name": "Swagger Petstore", "url": "http://petstore.swagger.io/v2", }, @@ -108,14 +119,17 @@ Object { "tags": Array [ Object { "description": "Everything about your Pets", + "id": "bfc2f4aa9bced", "name": "pet", }, Object { "description": "Access to Petstore orders", + "id": "20c6cf86f6d6d", "name": "store", }, Object { "description": "Operations about user", + "id": "10e73f5f26c07", "name": "user", }, ], diff --git a/src/oas/__tests__/accessors.test.ts b/src/oas/__tests__/accessors.test.ts index 29879b23..f608f4b0 100644 --- a/src/oas/__tests__/accessors.test.ts +++ b/src/oas/__tests__/accessors.test.ts @@ -1,80 +1,116 @@ -import { getValidOasParameters } from '../accessors'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../context'; +import { Fragment } from '../../types'; +import { createOasParamsIterator } from '../accessors'; +import { resolveRef } from '../resolver'; import { OasVersion } from '../types'; -describe('getOasParameters', () => { - it('should return empty array', () => { - expect(getValidOasParameters({}, OasVersion.OAS2, undefined, undefined)).toEqual([]); - }); +const getValidOasParameters = (document: { paths: { '/': { get: Fragment } } }, spec: OasVersion) => + Array.from( + createOasParamsIterator(spec as any).call( + createContext(document, resolveRef, DEFAULT_ID_GENERATOR), + document.paths['/'], + document.paths['/'].get, + ), + ); - it('should fallback to operation parameters', () => { - expect( - getValidOasParameters( - {}, - OasVersion.OAS2, - [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, - ], - undefined, - ), - ).toEqual([ - { - in: 'header', - name: 'n1', - }, - { - in: 'query', - name: 'n2', - }, - ]); - }); +describe('createOasParamsIterator', () => { + describe.each([OasVersion.OAS2, OasVersion.OAS3])('given %s spec', spec => { + it('should return empty array', () => { + const document = { + paths: { + '/': { + get: {}, + }, + }, + }; - it('should fallback to path parameters', () => { - expect( - getValidOasParameters({}, OasVersion.OAS2, undefined, [ - { name: 'n1', in: 'header' }, - { name: 'n2', in: 'query' }, - ]), - ).toEqual([ - { - in: 'header', - name: 'n1', - }, - { - in: 'query', - name: 'n2', - }, - ]); - }); + expect(getValidOasParameters(document, spec)).toEqual([]); + }); + + it('should fallback to operation parameters', () => { + const document = { + paths: { + '/': { + get: { + parameters: [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, + ], + }, + }, + }, + }; + + expect(getValidOasParameters(document, spec)).toEqual([ + { + in: 'header', + name: 'n1', + }, + { + in: 'query', + name: 'n2', + }, + ]); + }); + + it('should fallback to path parameters', () => { + const document = { + paths: { + '/': { + parameters: [ + { name: 'n1', in: 'header' }, + { name: 'n2', in: 'query' }, + ], + get: {}, + }, + }, + }; + + expect(getValidOasParameters(document, spec)).toEqual([ + { + in: 'header', + name: 'n1', + }, + { + in: 'query', + name: 'n2', + }, + ]); + }); + + it('should prefer operation parameters', () => { + const document = { + paths: { + '/': { + parameters: [ + { name: 'n1', in: 'query', type: 'string' }, + { name: 'np3', in: 'header' }, + ], + get: { + parameters: [ + { name: 'n1', in: 'query', type: 'array' }, + { name: 'no2', in: 'header' }, + ], + }, + }, + }, + }; - it('should prefer operation parameters', () => { - expect( - getValidOasParameters( - {}, - OasVersion.OAS3, - [ - { name: 'n1', in: 'query', type: 'array' }, - { name: 'no2', in: 'header' }, - ], - [ - { name: 'n1', in: 'query', type: 'string' }, - { name: 'np3', in: 'header' }, - ], - ), - ).toEqual([ - { - in: 'query', - name: 'n1', - type: 'array', - }, - { - in: 'header', - name: 'no2', - }, - { - in: 'header', - name: 'np3', - }, - ]); + expect(getValidOasParameters(document, spec)).toEqual([ + { + in: 'query', + name: 'n1', + type: 'array', + }, + { + in: 'header', + name: 'no2', + }, + { + in: 'header', + name: 'np3', + }, + ]); + }); }); }); diff --git a/src/oas/__tests__/fixtures/oas2-kitchen-sink.json b/src/oas/__tests__/fixtures/oas2-kitchen-sink.json index 3eef66d3..77415bab 100644 --- a/src/oas/__tests__/fixtures/oas2-kitchen-sink.json +++ b/src/oas/__tests__/fixtures/oas2-kitchen-sink.json @@ -1,4 +1,5 @@ { + "x-stoplight-id": "abc", "swagger": "2.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", diff --git a/src/oas/__tests__/fixtures/oas3-kitchen-sink.json b/src/oas/__tests__/fixtures/oas3-kitchen-sink.json index de2dca89..14c550d3 100644 --- a/src/oas/__tests__/fixtures/oas3-kitchen-sink.json +++ b/src/oas/__tests__/fixtures/oas3-kitchen-sink.json @@ -1,4 +1,5 @@ { + "x-stoplight-id": "abc", "openapi": "3.0.0", "info": { "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 2a0b0884..44471478 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -15,6 +15,6 @@ describe('oas service', () => { it('openapi v2 and v3 should transform in the same way', () => { const oas2MappedService = transformOas2Service({ document: oas2KitchenSinkJson as any }); const oas3MappedService = transformOas3Service({ document: oas3KitchenSinkJson as any }); - expect(oas2MappedService).toEqual(oas3MappedService); + expect(oas2MappedService).toStrictEqual(oas3MappedService); }); }); diff --git a/src/oas/__tests__/tags.test.ts b/src/oas/__tests__/tags.test.ts index 9efcbaa5..852c3a0a 100644 --- a/src/oas/__tests__/tags.test.ts +++ b/src/oas/__tests__/tags.test.ts @@ -1,7 +1,9 @@ -import { createContext } from '../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../context'; +import { resolveRef } from '../resolver'; import { translateToTags as _translateToTags } from '../tags'; -const translateToTags = (tags: unknown) => _translateToTags.call(createContext({}), tags); +const translateToTags = (tags: unknown) => + _translateToTags.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), tags); describe('translateToTags', () => { describe.each([2, null, {}, '', 0])('when tags property is not an array', tags => { @@ -13,6 +15,7 @@ describe('translateToTags', () => { it('should filter out invalid values', () => { expect(translateToTags([{}, null, 'foo'])).toStrictEqual([ { + id: expect.any(String), name: 'foo', }, ]); @@ -21,12 +24,15 @@ describe('translateToTags', () => { it('should normalize values', () => { expect(translateToTags([0, 'foo', true])).toStrictEqual([ { + id: expect.any(String), name: '0', }, { + id: expect.any(String), name: 'foo', }, { + id: expect.any(String), name: 'true', }, ]); @@ -35,12 +41,15 @@ describe('translateToTags', () => { it('should translate array of strings to tags', () => { expect(translateToTags(['a', 'b', 'c'])).toStrictEqual([ { + id: expect.any(String), name: 'a', }, { + id: expect.any(String), name: 'b', }, { + id: expect.any(String), name: 'c', }, ]); diff --git a/src/oas/accessors.ts b/src/oas/accessors.ts index 50675aad..63acf593 100644 --- a/src/oas/accessors.ts +++ b/src/oas/accessors.ts @@ -1,50 +1,44 @@ import type { Extensions } from '@stoplight/types'; -import { Fragment } from '../types'; -import { entries, maybeResolveLocalRef } from '../utils'; +import { Fragment, TransformerContext } from '../types'; +import { entries } from '../utils'; import { isValidOas2Param, isValidOas3Param, Oas2ParamBase, Oas3ParamBase, ParamBase } from './guards'; import { OasVersion } from './types'; const ROOT_EXTENSIONS = ['x-internal']; -function getParameters(document: Fragment, spec: OasVersion.OAS2, params: unknown): Oas2ParamBase[]; -function getParameters(document: Fragment, spec: OasVersion.OAS3, params: unknown): Oas3ParamBase[]; -function getParameters(document: Fragment, spec: OasVersion, params: unknown): ParamBase[] { - if (!Array.isArray(params)) return []; +const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; - const resolved = params.map(maybeResolveLocalRef.bind(null, document)); - return spec === OasVersion.OAS2 ? resolved.filter(isValidOas2Param) : resolved.filter(isValidOas3Param); -} +type OasParamsIterator = (this: TransformerContext, path: Fragment, operation: Fragment) => Iterable; -const getIdForParameter = (param: ParamBase) => `${param.name}-${param.in}`; +export function createOasParamsIterator(spec: OasVersion.OAS2): OasParamsIterator; +export function createOasParamsIterator(spec: OasVersion.OAS3): OasParamsIterator; +export function createOasParamsIterator(spec: OasVersion): OasParamsIterator { + return function* (path, operation) { + const seenParams = new Set(); + const { parentId, context } = this; + const opParams = Array.isArray(operation.parameters) ? operation.parameters : []; + const params = [...opParams, ...(Array.isArray(path.parameters) ? path.parameters : [])]; + + for (let i = 0; i < params.length; i++) { + const param = this.maybeResolveLocalRef(params[i]); + if (!(spec === OasVersion.OAS2 ? isValidOas2Param : isValidOas3Param)(param)) continue; + + const key = getIdForParameter(param); + + if (seenParams.has(key)) continue; + seenParams.add(key); + + if (this.context !== 'service') { + this.context = i < opParams.length ? 'operation' : 'path'; + } + + yield param; + } -export function getValidOasParameters( - document: Fragment, - spec: OasVersion.OAS2, - operationParams: unknown, - pathParams: unknown, -): Oas2ParamBase[]; -export function getValidOasParameters( - document: Fragment, - spec: OasVersion.OAS3, - operationParams: unknown, - pathParams: unknown, -): Oas3ParamBase[]; -export function getValidOasParameters( - document: Fragment, - spec: any, - operationParams: unknown, - pathParams: unknown, -): ParamBase[] { - const uniqueParameters: Record = {}; - - const params = [...getParameters(document, spec, operationParams), ...getParameters(document, spec, pathParams)]; - - for (const param of params) { - uniqueParameters[getIdForParameter(param)] ??= param; - } - - return Object.values(uniqueParameters); + this.context = context; + this.parentId = parentId; + }; } export function getExtensions(target: unknown): Extensions { diff --git a/src/oas/operation.ts b/src/oas/operation.ts index 94bb6864..8f8aa763 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,9 +1,14 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, IHttpOperation } from '@stoplight/types'; -import type { OpenAPIObject } from 'openapi3-ts'; +import pickBy = require('lodash.pickby'); +import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; +import { isBoolean, isString } from '../guards'; import type { HttpOperationTransformer } from '../types'; +import { TranslateFunction } from '../types'; +import { getExtensions } from './accessors'; +import { translateToTags } from './tags'; const DEFAULT_METHODS = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch', 'trace']; @@ -32,3 +37,57 @@ export function transformOasOperations( ); }); } + +function wipePathParams(p: string) { + return p.replace(/({)[^}]+(?=})/g, '$1'); +} + +export const transformOasOperation: TranslateFunction< + DeepPartial | DeepPartial, + [path: string, method: string], + Omit +> = function (path: string, method: string) { + const pathObj = this.maybeResolveLocalRef(this.document?.paths?.[path]) as PathsObject; + if (typeof pathObj !== 'object' || pathObj === null) { + throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); + } + + const operation = this.maybeResolveLocalRef(pathObj[method]) as OperationObject; + if (!operation) { + throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); + } + + const reducedPath = wipePathParams(path); + const serviceId = (this.ids.service = String(this.document['x-stoplight-id'])); + this.ids.path = this.generateId(`http_path-${this.ids.service}-${reducedPath}`); + const operationId = (this.ids.operation = this.generateId(`http_operation-${serviceId}-${method}-${reducedPath}`)); + + this.context = 'operation'; + + return { + id: operationId, + + method, + path, + + tags: translateToTags.call(this, operation.tags), + extensions: getExtensions(operation), + + ...pickBy( + { + deprecated: operation.deprecated, + internal: operation['x-internal'], + }, + isBoolean, + ), + + ...pickBy( + { + iid: operation.operationId, + description: operation.description, + summary: operation.summary, + }, + isString, + ), + }; +}; diff --git a/src/oas/resolver.ts b/src/oas/resolver.ts new file mode 100644 index 00000000..50cc837e --- /dev/null +++ b/src/oas/resolver.ts @@ -0,0 +1,31 @@ +import { resolveInlineRefWithLocation } from '@stoplight/json'; +import type { JsonPath } from '@stoplight/types'; + +import type { AvailableContext, RefResolver } from '../types'; + +function inferContext(path: JsonPath): AvailableContext { + if (path.length < 2 || path[0] !== 'paths') return 'service'; + if (path.length === 2 || path[3] === 'parameters' || path[3] === 'servers') return 'path'; + return 'operation'; +} + +const SHARED_COMPONENTS_KEYS = new WeakMap(); + +export function getSharedKey(value: object) { + return SHARED_COMPONENTS_KEYS.get(value); +} + +export const resolveRef: RefResolver = function (target) { + const { value: resolved, location } = resolveInlineRefWithLocation(this.document, target.$ref); + + const context = inferContext(location); + if (context !== null && this.context !== context) { + this.context = context; + } + + if (typeof resolved === 'object' && resolved !== null && context === 'service') { + SHARED_COMPONENTS_KEYS.set(resolved, location[0] === 'components' ? location[2] : location[1]); + } + + return resolved; +}; diff --git a/src/oas/service.ts b/src/oas/service.ts index c6cb6743..c8fc8b81 100644 --- a/src/oas/service.ts +++ b/src/oas/service.ts @@ -13,9 +13,12 @@ import { translateLogo } from './transformers/translateLogo'; export const transformOasService: TranslateFunction | DeepPartial, [], IHttpService> = function () { const document = this.document; + const id = String(document['x-stoplight-id']); + this.ids.service = id; + this.parentId = id; const httpService: IHttpService = { - id: '?http-service-id?', + id, version: document.info?.version ?? '', name: document.info?.title ?? 'no-title', diff --git a/src/oas/tags.ts b/src/oas/tags.ts index 7f1ec25d..573ca598 100644 --- a/src/oas/tags.ts +++ b/src/oas/tags.ts @@ -2,20 +2,22 @@ import { isPlainObject } from '@stoplight/json'; import type { INodeTag, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import { withContext } from '../context'; import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; import type { ArrayCallbackParameters, Fragment, TranslateFunction } from '../types'; -export const translateTag: TranslateFunction< - Fragment, - ArrayCallbackParameters, - Optional -> = function (tag) { - if (tag === null || !isSerializablePrimitive(tag)) return; +const translateTag = withContext, Optional>>( + function (tag) { + if (tag === null || !isSerializablePrimitive(tag)) return; - return { - name: String(tag), - }; -}; + const name = String(tag); + + return { + id: this.generateId(`tag-${this.ids.service}-${name}`), + name, + }; + }, +); export const translateTagDefinition: TranslateFunction< Fragment, diff --git a/src/oas/transformers/examples.ts b/src/oas/transformers/examples.ts new file mode 100644 index 00000000..99b1dc70 --- /dev/null +++ b/src/oas/transformers/examples.ts @@ -0,0 +1,18 @@ +import { DeepPartial, INodeExample } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; +import { Spec } from 'swagger-schema-official'; + +import { withContext } from '../../context'; +import { TranslateFunction } from '../../types'; + +export const translateToDefaultExample = withContext< + TranslateFunction, [key: string, value: unknown], INodeExample> +>(function (key, value) { + const resolvedValue = this.maybeResolveLocalRef(value); + + return { + id: this.generateId(`example-${this.parentId}-${key}`), + value: resolvedValue, + key, + }; +}); diff --git a/src/oas/transformers/schema/__tests__/schema.spec.ts b/src/oas/transformers/schema/__tests__/schema.spec.ts index bea202ba..cca91501 100644 --- a/src/oas/transformers/schema/__tests__/schema.spec.ts +++ b/src/oas/transformers/schema/__tests__/schema.spec.ts @@ -1,8 +1,10 @@ -import { createContext } from '../../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../../context'; +import { resolveRef } from '../../../resolver'; import { translateSchemaObject } from '..'; import type { OASSchemaObject } from '../types'; -const translate = (schemaObject: OASSchemaObject) => translateSchemaObject.call(createContext({}), schemaObject); +const translate = (schemaObject: OASSchemaObject) => + translateSchemaObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), schemaObject); describe('translateSchemaObject', () => { it('should translate id', () => { @@ -16,6 +18,7 @@ describe('translateSchemaObject', () => { }, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -36,6 +39,7 @@ describe('translateSchemaObject', () => { exclusiveMaximum: true, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', exclusiveMinimum: 2, @@ -68,6 +72,7 @@ describe('translateSchemaObject', () => { ], }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', anyOf: [ { @@ -110,6 +115,7 @@ describe('translateSchemaObject', () => { }, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { exclusiveMinimum: { @@ -152,6 +158,7 @@ describe('translateSchemaObject', () => { 'x-nullable': true, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: ['string', 'null'], }); @@ -164,6 +171,7 @@ describe('translateSchemaObject', () => { 'x-example': 'Cat', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', examples: ['Cat'], @@ -179,6 +187,7 @@ describe('translateSchemaObject', () => { nullable: true, }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: ['string', 'null'], }); @@ -191,6 +200,7 @@ describe('translateSchemaObject', () => { example: 'Cat', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', examples: ['Cat'], @@ -204,6 +214,7 @@ describe('translateSchemaObject', () => { format: 'base64', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', contentEncoding: 'base64', @@ -217,6 +228,7 @@ describe('translateSchemaObject', () => { format: 'binary', }), ).toStrictEqual({ + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', contentMediaType: 'application/octet-stream', diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index cd12e29c..3d5b2bea 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -4,7 +4,9 @@ import type { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema'; import type { OpenAPIObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; +import { withContext } from '../../../context'; import type { TranslateFunction } from '../../../types'; +import { getSharedKey } from '../../resolver'; import keywords from './keywords'; import type { OASSchemaObject } from './types'; @@ -14,28 +16,46 @@ type InternalOptions = { structs: string[]; }; +const CACHE = new WeakMap(); + // Convert from OpenAPI 2.0, OpenAPI 3.0 `SchemaObject` or JSON Schema Draft4/6 to JSON Schema Draft 7 // This converter shouldn't make any differences to Schema objects defined in OpenAPI 3.1, excepts when jsonSchemaDialect is provided. -export const translateSchemaObject: TranslateFunction< - DeepPartial, - [schema: OASSchemaObject], - JSONSchema7 -> = function (schema) { +export const translateSchemaObject = withContext< + TranslateFunction< + DeepPartial, + [schema: OASSchemaObject], + JSONSchema7 + > +>(function (schema) { const document = this.document; + const resolvedSchema = this.maybeResolveLocalRef(schema); + if (!isPlainObject(resolvedSchema)) return {}; + let cached = CACHE.get(resolvedSchema); + if (cached) { + return { ...cached }; + } + + const actualKey = this.context === 'service' ? getSharedKey(resolvedSchema) : ''; + const id = this.generateId(`schema-${this.parentId}-${actualKey}`); if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { - return { + cached = { $schema: document.jsonSchemaDialect, // let's assume it's draft 7, albeit it might be draft 2020-12 or 2019-09. // it's a safe bet, because there was only _one_ relatively minor breaking change introduced between Draft 7 and 2020-12. - ...(schema as JSONSchema7), + ...(resolvedSchema as JSONSchema7), + 'x-stoplight-id': id, }; + } else { + cached = convertSchema(resolvedSchema); + cached['x-stoplight-id'] = id; } - return convertSchema(schema); -}; + CACHE.set(resolvedSchema, cached); + return cached; +}); -export function convertSchema(schema: OASSchemaObject) { +export function convertSchema(schema: OASSchemaObject): JSONSchema7 { const clonedSchema = _convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); diff --git a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap index 53357496..1acc1f56 100644 --- a/src/oas2/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas2/__tests__/__snapshots__/operation.test.ts.snap @@ -5,7 +5,7 @@ Object { "deprecated": true, "description": "odesc", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "oid", "method": "get", "path": "/users/{userId}", @@ -22,10 +22,12 @@ Object { Object { "examples": Array [ Object { + "id": Any, "key": "example", "value": Object {}, }, ], + "id": Any, "mediaType": "", "schema": Object {}, }, @@ -33,14 +35,17 @@ Object { "description": "desc", "headers": Array [ Object { + "id": Any, "name": "header", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "integer", + "x-stoplight-id": Any, }, "style": "simple", }, ], + "id": Any, }, ], "security": Array [ @@ -54,12 +59,14 @@ Object { }, }, }, + "id": Any, "key": "petstore_auth", "type": "oauth2", }, ], Array [ Object { + "id": Any, "in": "header", "key": "api_key", "name": "api_key_name", diff --git a/src/oas2/__tests__/guards.test.ts b/src/oas2/__tests__/guards.test.ts deleted file mode 100644 index 67051a85..00000000 --- a/src/oas2/__tests__/guards.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { isSecurityScheme, isTagObject } from '../guards'; - -describe('guards', () => { - describe('isSecurityScheme()', () => { - it('should return true for scheme with type', () => { - expect( - isSecurityScheme({ - type: 'apiKey', - }), - ).toEqual(true); - }); - - it('should return false for non object', () => { - expect(isSecurityScheme(undefined)).toEqual(false); - }); - - it('should return false for missing type property', () => { - expect(isSecurityScheme({ foo: 'bar' })).toEqual(false); - }); - }); - - describe('isTagObject()', () => { - it('should return true for scheme with name', () => { - expect( - isTagObject({ - name: 'foo', - }), - ).toEqual(true); - }); - - it('should return false for non object', () => { - expect(isTagObject(undefined)).toEqual(false); - }); - - it('should return false for missing name property', () => { - expect(isTagObject({ foo: 'bar' })).toEqual(false); - }); - }); -}); diff --git a/src/oas2/__tests__/operation.test.ts b/src/oas2/__tests__/operation.test.ts index 044016c9..a43e338b 100644 --- a/src/oas2/__tests__/operation.test.ts +++ b/src/oas2/__tests__/operation.test.ts @@ -68,7 +68,44 @@ describe('transformOas2Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + responses: [ + { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + }, + ], + headers: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + ], + security: [ + [ + { + id: expect.any(String), + }, + ], + [ + { + id: expect.any(String), + }, + ], + ], + }); }); it('should properly translate operation with no response body', () => { @@ -155,7 +192,7 @@ describe('transformOas2Operation', () => { extensions: {}, }), { - id: '?http-operation-id?', + id: expect.any(String), path: '/users/{userId}', method: 'put', request: { @@ -198,15 +235,17 @@ describe('transformOas2Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/users/{userId}', request: { cookie: [], headers: [ { + id: expect.any(String), name: 'name', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', }, style: 'simple', diff --git a/src/oas2/__tests__/service.test.ts b/src/oas2/__tests__/service.test.ts index 754cd2f5..161e5067 100644 --- a/src/oas2/__tests__/service.test.ts +++ b/src/oas2/__tests__/service.test.ts @@ -1,11 +1,13 @@ import { OpenAPIObject } from 'openapi3-ts'; import { Spec } from 'swagger-schema-official'; +import { Fragment } from '../../types'; import { transformOas2Service } from '../service'; describe('oas2 service', () => { it('should handle non array schemes', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'def', schemes: 2 as any, }; @@ -14,14 +16,15 @@ describe('oas2 service', () => { document, }), ).toStrictEqual({ - id: '?http-service-id?', + id: 'def', name: 'no-title', version: '', }); }); it('should accept empty title', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'abc', host: 'petstore.swagger.io', basePath: '/v2', info: { @@ -36,15 +39,17 @@ describe('oas2 service', () => { document, }), ).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', servers: [ { + id: expect.any(String), name: '', url: 'https://petstore.swagger.io/v2', }, { + id: expect.any(String), name: '', url: 'http://petstore.swagger.io/v2', }, @@ -53,20 +58,22 @@ describe('oas2 service', () => { }); it('should handle invalid document securities gracefully', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'abc', securityDefinitions: {}, security: ['API-Key'] as any, }; expect(transformOas2Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', }); }); it('filters out scopes', () => { - const document: Partial = { + const document: Partial & Fragment = { + 'x-stoplight-id': 'def', swagger: '2.0', securityDefinitions: { 'API Key': { @@ -88,6 +95,7 @@ describe('oas2 service', () => { describe('x-logo support', () => { it('should support x-logo', () => { const document: Partial = { + 'x-stoplight-id': 'abc', info: { title: 'no-title', version: '1.0.0', @@ -101,7 +109,7 @@ describe('oas2 service', () => { }; expect(transformOas2Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '1.0.0', logo: { @@ -114,6 +122,7 @@ describe('oas2 service', () => { }); it('should provide default values for href and altText', () => { const document: Partial = { + 'x-stoplight-id': 'abc', info: { title: 'no-title', version: '1.0.0', @@ -125,7 +134,7 @@ describe('oas2 service', () => { }; expect(transformOas2Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', contact: { url: 'https://stoplight.io', diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index bbcfe522..1230a42b 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; -import { DeepPartial, Dictionary } from '@stoplight/types'; -import { Operation, Schema, Security, Spec } from 'swagger-schema-official'; +import type { DeepPartial } from '@stoplight/types'; +import type { Operation, Security, Spec } from 'swagger-schema-official'; import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../guards'; @@ -68,11 +68,11 @@ function getProducesOrConsumes( return mimeTypes.flat().filter(isString); } -export function getExamplesFromSchema(data: Schema & { 'x-examples'?: Dictionary }): Record { +export function getExamplesFromSchema(data: unknown): Record { if (!isPlainObject(data)) return {}; return { - ...('x-examples' in data && isPlainObject(data['x-examples']) && { ...data['x-examples'] }), + ...(isPlainObject(data['x-examples']) && { ...data['x-examples'] }), ...('example' in data && { default: data.example }), }; } diff --git a/src/oas2/guards.ts b/src/oas2/guards.ts index bf5d124f..cdfc6da3 100644 --- a/src/oas2/guards.ts +++ b/src/oas2/guards.ts @@ -33,3 +33,11 @@ export function isPathParam(param: unknown): param is Oas2ParamBase & { in: 'pat export function isHeaderParam(param: unknown): param is Oas2ParamBase & { in: 'header' } { return isValidOas2Param(param) && param.in === 'header'; } + +export function isBodyParam(param: unknown): param is Oas2ParamBase & { in: 'body' } { + return isValidOas2Param(param) && param.in === 'body'; +} + +export function isFormDataParam(param: unknown): param is Oas2ParamBase & { in: 'formData' } { + return isValidOas2Param(param) && param.in === 'formData'; +} diff --git a/src/oas2/index.ts b/src/oas2/index.ts index 0e972888..f2e98a7a 100644 --- a/src/oas2/index.ts +++ b/src/oas2/index.ts @@ -1,3 +1,2 @@ export * from './operation'; export * from './service'; -export * from './transformers/params'; diff --git a/src/oas2/operation.ts b/src/oas2/operation.ts index 44365397..0ee05d52 100644 --- a/src/oas2/operation.ts +++ b/src/oas2/operation.ts @@ -1,64 +1,32 @@ -import { isPlainObject } from '@stoplight/json'; -import type { IHttpOperation } from '@stoplight/types'; +import type { DeepPartial, IHttpOperation } from '@stoplight/types'; import type { Spec } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; -import { isBoolean, isString } from '../guards'; -import { getExtensions } from '../oas/accessors'; -import { transformOasOperations } from '../oas/operation'; -import { translateToTags } from '../oas/tags'; +import { createContext, DEFAULT_ID_GENERATOR } from '../context'; +import { transformOasOperation, transformOasOperations } from '../oas/operation'; +import { resolveRef } from '../oas/resolver'; import { Oas2HttpOperationTransformer } from '../oas/types'; -import { maybeResolveLocalRef } from '../utils'; +import type { Fragment } from '../types'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; import { translateToServers } from './transformers/servers'; -export function transformOas2Operations(document: Spec): IHttpOperation[] { +export function transformOas2Operations(document: DeepPartial): IHttpOperation[] { return transformOasOperations(document, transformOas2Operation); } -export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]); - if (!isPlainObject(pathObj)) { - throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); - } - - const operation = maybeResolveLocalRef(document, pathObj[method]); - if (!isPlainObject(operation)) { - throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); - } - - const ctx = createContext(document); +export const transformOas2Operation: Oas2HttpOperationTransformer = ({ document: _document, path, method }) => { + const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); + const httpOperation = transformOasOperation.call(ctx, path, method); + const pathObj = ctx.maybeResolveLocalRef(ctx.document.paths![path]) as Fragment; + const operation = ctx.maybeResolveLocalRef(pathObj[method]) as Fragment; return { - id: '?http-operation-id?', - method, - path, + ...httpOperation, responses: translateToResponses.call(ctx, operation), servers: translateToServers.call(ctx, operation), request: translateToRequest.call(ctx, pathObj, operation), - tags: translateToTags.call(ctx, operation.tags), security: translateToSecurities.call(ctx, operation.security), - extensions: getExtensions(operation), - - ...pickBy( - { - deprecated: operation.deprecated, - internal: operation['x-internal'], - }, - isBoolean, - ), - - ...pickBy( - { - iid: operation.operationId, - description: operation.description, - summary: operation.summary, - }, - isString, - ), }; }; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index a67f48d6..344282f5 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,8 +1,9 @@ import { isPlainObject } from '@stoplight/json'; import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../context'; import { isNonNullable, isString } from '../guards'; +import { resolveRef } from '../oas/resolver'; import { transformOasService } from '../oas/service'; import { Oas2HttpServiceTransformer } from '../oas/types'; import { entries } from '../utils'; @@ -10,7 +11,7 @@ import { translateToSingleSecurity } from './transformers/securities'; import { translateToServer } from './transformers/servers'; export const transformOas2Service: Oas2HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document); + const ctx = createContext(document, resolveRef, DEFAULT_ID_GENERATOR); const httpService = transformOasService.call(ctx); if (document.info?.license) { diff --git a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap index 0fe08866..e212b60d 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/params.test.ts.snap @@ -6,26 +6,31 @@ Object { Object { "examples": Array [ Object { + "id": Any, "key": "example-1", "value": Object { "hello": "world", }, }, Object { + "id": Any, "key": "example-2", "value": Object { "foo": "bar", }, }, ], + "id": Any, "mediaType": "*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "e-mail", + "x-stoplight-id": Any, }, }, ], "description": "descr", + "id": Any, "required": true, } `; @@ -35,14 +40,17 @@ Object { "contents": Array [ Object { "examples": Array [], + "id": Any, "mediaType": "*", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "format": "e-mail", + "x-stoplight-id": Any, }, }, ], "description": "descr", + "id": Any, "required": true, } `; @@ -50,12 +58,14 @@ Object { exports[`params.translator translateToHeaderParam should translate header param 1`] = ` Object { "description": "desc", + "id": Any, "name": "name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "minimum": 12, "type": "integer", + "x-stoplight-id": Any, }, "style": "simple", } @@ -67,19 +77,23 @@ exports[`params.translator translateToHeaderParams should translate to multiple Array [ Object { "description": "a description", + "id": "117996bbb6f45", "name": "header-name", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9259b14a1615b", }, "style": "simple", }, Object { "description": "another description", + "id": "dce8b374eb7d6", "name": "plain-tex", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "31f792c907f10", }, "style": "simple", }, @@ -90,10 +104,12 @@ exports[`params.translator translateToHeaderParams should translate to simple he Array [ Object { "description": "a description", + "id": "117996bbb6f45", "name": "header-name", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": "9259b14a1615b", }, "style": "simple", }, @@ -103,11 +119,13 @@ Array [ exports[`params.translator translateToPathParameter should translate 1`] = ` Object { "description": "descr", + "id": Any, "name": "name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", + "x-stoplight-id": Any, }, "style": "simple", } diff --git a/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap index 88edbf9d..bf1ecd57 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -1,135 +1,135 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`responses should translate to multiple responses 1`] = ` -Array [ - Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [ - Object { - "key": "application/json", - "value": Object {}, - }, - ], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", +Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [ + Object { + "id": Any, + "key": "application/json", + "value": Object {}, }, + ], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - Object { - "examples": Array [], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, - }, - ], - "description": "d1", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", + }, + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - }, - Object { - "code": "r2", - "contents": Array [ - Object { - "examples": Array [], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, + }, + ], + "description": "d1", + "headers": Array [], + "id": Any, +} +`; + +exports[`responses should translate to multiple responses 2`] = ` +Object { + "code": "r2", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - Object { - "examples": Array [ - Object { - "key": "application/xml", - "value": Object {}, - }, - ], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", + }, + Object { + "examples": Array [ + Object { + "id": Any, + "key": "application/xml", + "value": Object {}, }, + ], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - "description": "d2", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", - }, - ], - }, -] + }, + ], + "description": "d2", + "headers": Array [], + "id": Any, +} `; exports[`responses should translate to response w/o examples 1`] = ` -Array [ - Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, - }, - Object { - "examples": Array [], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, +Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - "description": "d1", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", + }, + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - }, -] + }, + ], + "description": "d1", + "headers": Array [], + "id": Any, +} `; exports[`responses should translate to response w/o headers 1`] = ` -Array [ - Object { - "code": "r1", - "contents": Array [ - Object { - "examples": Array [], - "mediaType": "application/json", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - }, +Object { + "code": "r1", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "application/json", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - Object { - "examples": Array [ - Object { - "key": "application/xml", - "value": Object {}, - }, - ], - "mediaType": "application/xml", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", + }, + Object { + "examples": Array [ + Object { + "id": Any, + "key": "application/xml", + "value": Object {}, }, + ], + "id": Any, + "mediaType": "application/xml", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, - ], - "description": "d1", - "headers": Array [ - Object { - "name": "fake-header", - "style": "simple", - }, - ], - }, -] + }, + ], + "description": "d1", + "headers": Array [], + "id": Any, +} `; diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index 2b8158a8..518d59b1 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -1,7 +1,8 @@ import { DeepPartial, HttpParamStyles } from '@stoplight/types'; import { FormDataParameter, QueryParameter, Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateFromFormDataParameters as _translateFromFormDataParameters, translateToBodyParameter as _translateToBodyParameter, @@ -14,28 +15,28 @@ import { const translateFromFormDataParameters = ( document: DeepPartial, ...params: Parameters -) => _translateFromFormDataParameters.call(createContext(document), ...params); +) => _translateFromFormDataParameters.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToBodyParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToBodyParameter.call(createContext(document), ...params); +) => _translateToBodyParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToHeaderParam = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParam.call(createContext(document), ...params); + _translateToHeaderParam.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToHeaderParams = (document: DeepPartial, ...params: Parameters) => - _translateToHeaderParams.call(createContext(document), ...params); + _translateToHeaderParams.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToPathParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToPathParameter.call(createContext(document), ...params); +) => _translateToPathParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); const translateToQueryParameter = ( document: DeepPartial, ...params: Parameters -) => _translateToQueryParameter.call(createContext(document), ...params); +) => _translateToQueryParameter.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); describe('params.translator', () => { let consumes = ['*']; @@ -54,7 +55,12 @@ describe('params.translator', () => { description: 'desc', }, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); }); @@ -112,7 +118,17 @@ describe('params.translator', () => { }, consumes, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('should preserve readOnly flag in schema', () => { @@ -133,7 +149,13 @@ describe('params.translator', () => { ).toEqual( expect.objectContaining({ contents: expect.arrayContaining([ - expect.objectContaining({ schema: { $schema: 'http://json-schema.org/draft-07/schema#', readOnly: true } }), + expect.objectContaining({ + schema: { + 'x-stoplight-id': expect.any(String), + $schema: 'http://json-schema.org/draft-07/schema#', + readOnly: true, + }, + }), ]), }), ); @@ -163,7 +185,25 @@ describe('params.translator', () => { }, consumes, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); describe('schema examples', () => { @@ -194,8 +234,8 @@ describe('params.translator', () => { contents: expect.arrayContaining([ expect.objectContaining({ examples: [ - { key: 'example-1', value: { hello: 'world' } }, - { key: 'example-2', value: { foo: 'bar' } }, + { id: expect.any(String), key: 'example-1', value: { hello: 'world' } }, + { id: expect.any(String), key: 'example-2', value: { foo: 'bar' } }, ], }), ]), @@ -232,7 +272,7 @@ describe('params.translator', () => { expect.objectContaining({ contents: expect.arrayContaining([ expect.objectContaining({ - examples: [{ key: 'example-2', value: { foo: 'bar' } }], + examples: [{ id: expect.any(String), key: 'example-2', value: { foo: 'bar' } }], }), ]), }), @@ -284,6 +324,7 @@ describe('params.translator', () => { it('converts parameters into schema', () => { const expectedContent = { + id: expect.any(String), encodings: [ { explode: true, @@ -292,9 +333,11 @@ describe('params.translator', () => { }, ], schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { arr: { + 'x-stoplight-id': expect.any(String), description: 'desc', items: { type: 'number', @@ -304,12 +347,14 @@ describe('params.translator', () => { type: 'array', }, int: { + 'x-stoplight-id': expect.any(String), description: 'desc', maximum: 3, minimum: 0, type: 'integer', }, str: { + 'x-stoplight-id': expect.any(String), minLength: 1, default: '25-07-2019', description: 'desc', @@ -329,6 +374,7 @@ describe('params.translator', () => { consumes, ), ).toEqual({ + id: expect.any(String), contents: [ Object.assign({}, expectedContent, { mediaType: 'application/x-www-form-urlencoded' }), Object.assign({}, expectedContent, { mediaType: 'multipart/form-data' }), @@ -378,7 +424,12 @@ describe('params.translator', () => { type: 'string', }, ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); }); }); diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index 286e6788..a79c060d 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -6,7 +6,8 @@ import { QueryParameter, } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateFromFormDataParameters, translateToBodyParameter, @@ -19,7 +20,7 @@ import { translateToRequest as _translateToRequest } from '../request'; jest.mock('../params'); const translateToRequest = (path: Record, parameters: any[]) => { - const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }); + const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }, resolveRef, DEFAULT_ID_GENERATOR); return _translateToRequest.call(ctx, path, { parameters }); }; diff --git a/src/oas2/transformers/__tests__/responses.test.ts b/src/oas2/transformers/__tests__/responses.test.ts index 11881d59..95915e2d 100644 --- a/src/oas2/transformers/__tests__/responses.test.ts +++ b/src/oas2/transformers/__tests__/responses.test.ts @@ -1,23 +1,16 @@ -import { DeepPartial, HttpParamStyles, IHttpHeaderParam } from '@stoplight/types'; +import { DeepPartial } from '@stoplight/types'; import { Operation, Schema, Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; -import { translateToHeaderParams } from '../params'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToResponses as _translateToResponses } from '../responses'; -jest.mock('../params'); - const translateToResponses = (document: DeepPartial, responses: DeepPartial) => - _translateToResponses.call(createContext(document), { responses }); + _translateToResponses.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), { responses }); describe('responses', () => { - const fakeHeaderParams: IHttpHeaderParam[] = [{ name: 'fake-header', style: HttpParamStyles.Simple }]; const produces = ['application/json', 'application/xml']; - beforeEach(() => { - (translateToHeaderParams as jest.Mock).mockReturnValue(fakeHeaderParams); - }); - it('should translate to multiple responses', () => { const responses = translateToResponses( { produces }, @@ -41,38 +34,122 @@ describe('responses', () => { }, ); - expect(responses).toMatchSnapshot(); + expect(responses).toHaveLength(2); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + examples: [ + { + id: expect.any(String), + }, + ], + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + examples: [], + }, + ], + }); + expect(responses[1]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + examples: [ + { + id: expect.any(String), + }, + ], + }, + ], + }); }); it('should translate to response w/o headers', () => { - expect( - translateToResponses( - { produces }, + const responses = translateToResponses( + { produces }, + { + r1: { + description: 'd1', + examples: { + 'application/xml': {}, + }, + schema: {}, + }, + }, + ); + + expect(responses).toHaveLength(1); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ { - r1: { - description: 'd1', - examples: { - 'application/xml': {}, - }, - schema: {}, + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), }, + examples: [ + { + id: expect.any(String), + }, + ], }, - ), - ).toMatchSnapshot(); + ], + }); }); it('should translate to response w/o examples', () => { - expect( - translateToResponses( - { produces }, + const responses = translateToResponses( + { produces }, + { + r1: { + description: 'd1', + schema: {}, + }, + }, + ); + + expect(responses).toHaveLength(1); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ { - r1: { - description: 'd1', - schema: {}, + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), }, }, - ), - ).toMatchSnapshot(); + ], + }); }); describe('should keep foreign examples', () => { @@ -117,7 +194,9 @@ describe('responses', () => { }, }, ); - expect(responses[0].contents![0]).toHaveProperty('examples', [{ key: 'default', value: { name: 'value' } }]); + expect(responses[0].contents![0]).toHaveProperty('examples', [ + { id: expect.any(String), key: 'default', value: { name: 'value' } }, + ]); }); }); @@ -143,8 +222,8 @@ describe('responses', () => { }, ); expect(responses[0].contents![0]).toHaveProperty('examples', [ - { key: 'application/json', value: { name: 'examples value' } }, - { key: 'default', value: { name: 'example value' } }, + { id: expect.any(String), key: 'application/json', value: { name: 'examples value' } }, + { id: expect.any(String), key: 'default', value: { name: 'example value' } }, ]); }); }); @@ -171,8 +250,8 @@ describe('responses', () => { ); expect(responses[0].contents![0].examples).toHaveLength(2); expect(responses[0].contents![0].examples).toEqual([ - { key: 'application/json', value: {} }, - { key: 'application/i-have-no-clue', value: {} }, + { id: expect.any(String), key: 'application/json', value: {} }, + { id: expect.any(String), key: 'application/i-have-no-clue', value: {} }, ]); }); }); diff --git a/src/oas2/transformers/__tests__/securities.test.ts b/src/oas2/transformers/__tests__/securities.test.ts index 9c60c069..c47e799d 100644 --- a/src/oas2/transformers/__tests__/securities.test.ts +++ b/src/oas2/transformers/__tests__/securities.test.ts @@ -1,11 +1,12 @@ import { DeepPartial } from '@stoplight/types'; import { Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToSecurities as _translateToSecurities } from '../securities'; const translateToSecurities = (document: DeepPartial, ...params: Parameters) => - _translateToSecurities.call(createContext(document), ...params); + _translateToSecurities.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); describe('securities', () => { describe('translateToSecurities', () => { @@ -40,6 +41,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', @@ -67,6 +69,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'header', @@ -94,6 +97,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), name: '', type: 'apiKey', in: 'header', @@ -140,6 +144,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-flow-security', @@ -167,6 +172,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: {} } }, key: 'implicit-flow-security', @@ -195,6 +201,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { password: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'password-flow-security', @@ -223,6 +230,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { clientCredentials: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'application-flow-security', @@ -252,6 +260,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { authorizationCode: { @@ -317,6 +326,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', @@ -325,6 +335,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', @@ -333,6 +344,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', @@ -349,18 +361,21 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), scheme: 'basic', type: 'http', description: 'a description', key: 'basic-security', }, { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', type: 'oauth2', }, { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', diff --git a/src/oas2/transformers/__tests__/servers.test.ts b/src/oas2/transformers/__tests__/servers.test.ts index 11cd45d6..0f10b8bf 100644 --- a/src/oas2/transformers/__tests__/servers.test.ts +++ b/src/oas2/transformers/__tests__/servers.test.ts @@ -1,13 +1,14 @@ import type { DeepPartial } from '@stoplight/types'; import type { Spec } from 'swagger-schema-official'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToServers as _translateToServers } from '../servers'; type GlobalWithLocation = typeof global & { location?: Partial & { href: string } }; const translateToServers = (document: DeepPartial, ...params: Parameters) => - _translateToServers.call(createContext(document), ...params); + _translateToServers.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), ...params); describe('translateToServers', () => { afterAll(() => { @@ -20,7 +21,7 @@ describe('translateToServers', () => { href: 'https://www.someotherdomain.com?query=123', }; expect(translateToServers({ host: 'stoplight.io' }, { schemes: ['http'] })).toEqual([ - { url: 'http://stoplight.io' }, + { id: expect.any(String), url: 'http://stoplight.io' }, ]); }); @@ -29,9 +30,11 @@ describe('translateToServers', () => { translateToServers({ host: 'stoplight.io', basePath: '/base-path' }, { schemes: ['http', 'https'] }), ).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io/base-path', }, { + id: expect.any(String), url: 'https://stoplight.io/base-path', }, ]); @@ -49,9 +52,11 @@ describe('translateToServers', () => { ), ).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io/base-path', }, { + id: expect.any(String), url: 'https://stoplight.io/base-path', }, ]); @@ -62,6 +67,7 @@ describe('translateToServers', () => { translateToServers({ schemes: ['https'], host: 'stoplight.io', basePath: '/base-path' }, { schemes: ['http'] }), ).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io/base-path', }, ]); @@ -74,6 +80,7 @@ describe('translateToServers', () => { it('given no basePath should return servers', () => { expect(translateToServers({ schemes: ['http'], host: 'stoplight.io' }, {})).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io', }, ]); @@ -114,9 +121,11 @@ describe('translateToServers', () => { it('should handle invalid server basePath gracefully', () => { expect(translateToServers({ host: 'stoplight.io', basePath: 123 as any }, { schemes: ['http', 'https'] })).toEqual([ { + id: expect.any(String), url: 'http://stoplight.io', }, { + id: expect.any(String), url: 'https://stoplight.io', }, ]); diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index 9bbcd94d..894c3520 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -21,8 +21,10 @@ import type { import pickBy = require('lodash.pickby'); import pick = require('lodash.pick'); +import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; import { Oas2ParamBase } from '../../oas/guards'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; @@ -61,13 +63,15 @@ function chooseQueryParameterStyle( } } -export const translateToHeaderParam: Oas2TranslateFunction< - [param: DeepPartial & Oas2ParamBase & { in: 'header' }], - IHttpHeaderParam -> = function (param) { +export const translateToHeaderParam = withContext< + Oas2TranslateFunction<[param: DeepPartial & Oas2ParamBase & { in: 'header' }], IHttpHeaderParam> +>(function (param) { + const name = param.name; + return { + id: this.generateId(`http_header-${this.parentId}-${name}`), + name, style: HttpParamStyles.Simple, - name: param.name, ...buildSchemaForParameter.call(this, param), ...pickBy( @@ -77,7 +81,7 @@ export const translateToHeaderParam: Oas2TranslateFunction< isBoolean, ), }; -}; +}); const translateToHeaderParamsFromPair: Oas2TranslateFunction< ArrayCallbackParameters<[name: string, value: unknown]>, @@ -95,39 +99,60 @@ export const translateToHeaderParams: Oas2TranslateFunction<[headers: unknown], return entries(headers).map(translateToHeaderParamsFromPair, this).filter(isNonNullable); }; -export const translateToBodyParameter: Oas2TranslateFunction< - [body: BodyParameter, consumes: string[]], - IHttpOperationRequestBody -> = function (body, consumes) { - const examples = entries(body['x-examples'] || (body.schema ? getExamplesFromSchema(body.schema) : void 0)).map( - ([key, value]) => ({ key, value }), +export const translateToBodyParameter = withContext< + Oas2TranslateFunction<[body: BodyParameter, consumes: string[]], IHttpOperationRequestBody> +>(function (body, consumes) { + const examples = entries(body['x-examples'] || getExamplesFromSchema(body.schema)).map(([key, value]) => + translateToDefaultExample.call(this, key, value), ); - return pickBy({ - description: body.description, - required: body.required, - contents: consumes.map(mediaType => { - return { - mediaType, - schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, - examples, - }; - }), - }); -}; + return { + id: this.generateId(`http_request_body-${this.parentId}`), -export const translateFromFormDataParameters: Oas2TranslateFunction< - [parameters: FormDataParameter[], consumes: string[]], - IHttpOperationRequestBody -> = function (parameters, consumes) { - const finalBody: IHttpOperationRequestBody = { - contents: consumes.map(mediaType => ({ - mediaType, - schema: { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', + contents: consumes.map( + withContext(mediaType => { + return { + id: this.generateId(`http_media-${this.parentId}-${mediaType}`), + mediaType, + schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, + examples, + }; + }), + this, + ), + + ...pickBy( + { + required: body.required, + }, + isBoolean, + ), + + ...pickBy( + { + description: body.description, }, - })), + isString, + ), + }; +}); + +export const translateFromFormDataParameters = withContext< + Oas2TranslateFunction< + [parameters: (Oas2ParamBase & Partial)[], consumes: string[]], + IHttpOperationRequestBody + > +>(function (parameters, consumes) { + const finalBody: IHttpOperationRequestBody = { + id: this.generateId(`http_request_body-${this.parentId}`), + contents: consumes.map( + withContext(mediaType => ({ + id: this.generateId(`http_media-${this.parentId}-${mediaType}`), + mediaType, + schema: translateSchemaObject.call(this, { type: 'object' }), + })), + this, + ), }; return parameters.reduce((body, parameter) => { @@ -157,9 +182,9 @@ export const translateFromFormDataParameters: Oas2TranslateFunction< }); return body; }, finalBody); -}; +}); -function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { +function buildEncoding(parameter: Oas2ParamBase & Partial): IHttpEncoding | null { switch (parameter.collectionFormat) { case 'csv': return { @@ -189,31 +214,36 @@ function buildEncoding(parameter: FormDataParameter): IHttpEncoding | null { return null; } -export const translateToQueryParameter: Oas2TranslateFunction< - [query: DeepPartial & Oas2ParamBase], - IHttpQueryParam -> = function (query) { +export const translateToQueryParameter = withContext< + Oas2TranslateFunction<[query: DeepPartial & Oas2ParamBase], IHttpQueryParam> +>(function (param) { + const name = param.name; + return { - style: chooseQueryParameterStyle(query), - name: query.name, - ...buildSchemaForParameter.call(this, query), + id: this.generateId(`http_query-${this.parentId}-${name}`), + name, + style: chooseQueryParameterStyle(param), + + ...buildSchemaForParameter.call(this, param), ...pickBy( { - required: query.required, - allowEmptyValue: query.allowEmptyValue, + allowEmptyValue: param.allowEmptyValue, + required: param.required, }, isBoolean, ), }; -}; +}); + +export const translateToPathParameter = withContext< + Oas2TranslateFunction<[param: DeepPartial & Oas2ParamBase], IHttpPathParam> +>(function (param) { + const name = param.name; -export const translateToPathParameter: Oas2TranslateFunction< - [param: DeepPartial & Oas2ParamBase], - IHttpPathParam -> = function (param) { return { - name: param.name, + id: this.generateId(`http_path_param-${this.parentId}-${name}`), + name, style: HttpParamStyles.Simple, ...buildSchemaForParameter.call(this, param), @@ -225,7 +255,7 @@ export const translateToPathParameter: Oas2TranslateFunction< isBoolean, ), }; -}; +}); const buildSchemaForParameter: Oas2TranslateFunction< [param: DeepPartial], @@ -265,7 +295,6 @@ const buildSchemaForParameter: Oas2TranslateFunction< }, isBoolean, ), - ...pickBy( { description: param.description, diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index cdf894e3..d75b616e 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,12 +1,10 @@ import type { IHttpOperationRequest } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); -import type { BodyParameter, FormDataParameter } from 'swagger-schema-official'; import { isNonNullable } from '../../guards'; import { OasVersion } from '../../oas'; -import { getValidOasParameters } from '../../oas/accessors'; +import { createOasParamsIterator } from '../../oas/accessors'; import { getConsumes } from '../accessors'; -import { isHeaderParam, isPathParam, isQueryParam } from '../guards'; +import { isBodyParam, isFormDataParam, isHeaderParam, isPathParam, isQueryParam } from '../guards'; import { Oas2TranslateFunction } from '../types'; import { translateFromFormDataParameters, @@ -15,16 +13,17 @@ import { translateToPathParameter, translateToQueryParameter, } from './params'; +import pickBy = require('lodash.pickby'); +import { Oas2ParamBase } from '../../oas/guards'; + +const iterateOasParams = createOasParamsIterator(OasVersion.OAS2); export const translateToRequest: Oas2TranslateFunction< [path: Record, operation: Record], IHttpOperationRequest > = function (path, operation) { const consumes = getConsumes(this.document, operation); - const parameters = getValidOasParameters(this.document, OasVersion.OAS2, operation.parameters, path.parameters); - - const bodyParameter = parameters.find((p): p is BodyParameter => p.in === 'body'); - const formDataParameters = parameters.filter((p): p is FormDataParameter => p.in === 'formData'); + const parameters = iterateOasParams.call(this, operation, path); const params: Omit, 'body'> = { headers: [], @@ -33,14 +32,8 @@ export const translateToRequest: Oas2TranslateFunction< path: [], }; - let body; - // if 'body' and 'form data' defined prefer 'body' - if (!!bodyParameter) { - // There can be only one body parameter (taking first one) - body = translateToBodyParameter.call(this, bodyParameter, consumes); - } else if (!!formDataParameters.length) { - body = translateFromFormDataParameters.call(this, formDataParameters, consumes); - } + let bodyParameter; + const formDataParameters: (Oas2ParamBase & { in: 'formData' })[] = []; for (const param of parameters) { if (isQueryParam(param)) { @@ -49,9 +42,22 @@ export const translateToRequest: Oas2TranslateFunction< params.path.push(translateToPathParameter.call(this, param)); } else if (isHeaderParam(param)) { params.headers.push(translateToHeaderParam.call(this, param)); + } else if (isBodyParam(param)) { + bodyParameter = translateToBodyParameter.call(this, param, consumes); + } else if (isFormDataParam(param)) { + formDataParameters.push(param); } } + let body; + // if 'body' and 'form data' defined prefer 'body' + if (!!bodyParameter) { + // There can be only one body parameter (taking first one) + body = bodyParameter; + } else if (!!formDataParameters.length) { + body = translateFromFormDataParameters.call(this, formDataParameters, consumes); + } + return { ...params, diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index 34b58bd8..c41ed3a6 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,9 +1,12 @@ import { isPlainObject } from '@stoplight/json'; -import type { IHttpOperationResponse, Optional } from '@stoplight/types'; -import { DeepPartial } from '@stoplight/types'; -import { Operation } from 'swagger-schema-official'; +import type { DeepPartial, IHttpOperationResponse, IMediaTypeContent, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); +import type { Operation } from 'swagger-schema-official'; +import { withContext } from '../../context'; import { isNonNullable } from '../../guards'; +import { getSharedKey } from '../../oas/resolver'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { entries } from '../../utils'; import { getExamplesFromSchema, getProduces } from '../accessors'; @@ -11,29 +14,40 @@ import { isResponseObject } from '../guards'; import { Oas2TranslateFunction } from '../types'; import { translateToHeaderParams } from './params'; -const translateToResponse: Oas2TranslateFunction< - [produces: string[], statusCode: string, response: unknown], - Optional -> = function (produces, statusCode, response) { +const translateToResponse = withContext< + Oas2TranslateFunction<[produces: string[], statusCode: string, response: unknown], Optional> +>(function (produces, statusCode, response) { const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; + const actualKey = this.context === 'service' ? getSharedKey(resolvedResponse) : statusCode; + const headers = translateToHeaderParams.call(this, resolvedResponse.headers); - const objectifiedExamples = entries( - resolvedResponse.examples || (resolvedResponse.schema ? getExamplesFromSchema(resolvedResponse.schema) : void 0), - ).map(([key, value]) => ({ key, value })); + const objectifiedExamples = entries(resolvedResponse.examples || getExamplesFromSchema(resolvedResponse.schema)).map( + ([key, value]) => translateToDefaultExample.call(this, key, value), + ); const contents = produces - .map(produceElement => ({ - mediaType: produceElement, - schema: isPlainObject(resolvedResponse.schema) - ? translateSchemaObject.call(this, resolvedResponse.schema) - : void 0, - examples: objectifiedExamples.filter(example => example.key === produceElement), - })) + .map }>( + withContext(produceElement => ({ + id: this.generateId(`http_media-${this.parentId}-${produceElement}`), + mediaType: produceElement, + examples: objectifiedExamples.filter(example => example.key === produceElement), + ...pickBy( + { + schema: isPlainObject(resolvedResponse.schema) + ? translateSchemaObject.call(this, resolvedResponse.schema) + : undefined, + }, + isNonNullable, + ), + })), + this, + ) .filter(({ schema, examples }) => !!schema || examples.length > 0); const translatedResponses = { + id: this.generateId(`http_response-${this.parentId}-${actualKey}`), code: statusCode, description: resolvedResponse.description, headers, @@ -44,6 +58,7 @@ const translateToResponse: Oas2TranslateFunction< if (foreignExamples.length > 0) { if (translatedResponses.contents.length === 0) translatedResponses.contents[0] = { + id: this.generateId(`http_media-${this.parentId}-`), mediaType: '', schema: {}, examples: [], @@ -53,7 +68,7 @@ const translateToResponse: Oas2TranslateFunction< } return translatedResponses; -}; +}); export const translateToResponses: Oas2TranslateFunction< [operation: DeepPartial], diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index 939fdfd4..ed674d04 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -1,6 +1,7 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, + HttpSecurityScheme, IApiKeySecurityScheme, IBasicSecurityScheme, IOauth2SecurityScheme, @@ -17,11 +18,11 @@ import type { OAuth2PasswordSecurity, } from 'swagger-schema-official'; +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import { SecurityWithKey } from '../../oas3/accessors'; import { getSecurities } from '../accessors'; import { isSecurityScheme } from '../guards'; -import { Oas2TranslateFunction } from '../types'; +import type { Oas2TranslateFunction } from '../types'; export const translateToFlows: Oas2TranslateFunction<[security: Record], IOauthFlowObjects> = function (security) { @@ -58,14 +59,16 @@ export const translateToFlows: Oas2TranslateFunction<[security: Record & { key: string }], - IBasicSecurityScheme -> = function (security) { +export const translateToBasicSecurityScheme = withContext< + Oas2TranslateFunction<[security: DeepPartial & { key: string }], IBasicSecurityScheme> +>(function (security) { + const key = security.key; + return { + id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'http', scheme: 'basic', - key: security.key, + key, ...pickBy( { @@ -74,20 +77,22 @@ export const translateToBasicSecurityScheme: Oas2TranslateFunction< isString, ), }; -}; +}); const ACCEPTABLE_SECURITY_ORIGINS: ApiKeySecurity['in'][] = ['query', 'header']; -export const translateToApiKeySecurityScheme: Oas2TranslateFunction< - [security: DeepPartial & { key: string }], - Optional -> = function (security) { +export const translateToApiKeySecurityScheme = withContext< + Oas2TranslateFunction<[security: DeepPartial & { key: string }], Optional> +>(function (security) { if ('in' in security && security.in && ACCEPTABLE_SECURITY_ORIGINS.includes(security.in)) { + const key = security.key; + return { + id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'apiKey', in: security.in, - name: String(security.name || ''), - key: security.key, + name: isString(security.name) ? security.name : '', + key, ...pickBy( { @@ -99,24 +104,28 @@ export const translateToApiKeySecurityScheme: Oas2TranslateFunction< } return; -}; +}); const VALID_OAUTH2_FLOWS = ['implicit', 'password', 'application', 'accessCode']; -export const translateToOauth2SecurityScheme: Oas2TranslateFunction< - [ - security: DeepPartial< - OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity - > & { key: string }, - ], - Optional -> = function (security) { +export const translateToOauth2SecurityScheme = withContext< + Oas2TranslateFunction< + [ + security: DeepPartial< + OAuth2AccessCodeSecurity | OAuth2ApplicationSecurity | OAuth2ImplicitSecurity | OAuth2PasswordSecurity + > & { key: string }, + ], + Optional + > +>(function (security) { if (!security.flow || !VALID_OAUTH2_FLOWS.includes(security.flow)) return undefined; + const key = security.key; return { + id: this.generateId(`http_security-${this.ids.service}-${key}`), type: 'oauth2', flows: translateToFlows.call(this, security), - key: security.key, + key, ...pickBy( { @@ -125,11 +134,11 @@ export const translateToOauth2SecurityScheme: Oas2TranslateFunction< isString, ), }; -}; +}); export const translateToSingleSecurity: Oas2TranslateFunction< [security: unknown & { key: string }], - Optional + Optional > = function (security) { if (isSecurityScheme(security)) { switch (security.type) { @@ -145,7 +154,7 @@ export const translateToSingleSecurity: Oas2TranslateFunction< return; }; -export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = +export const translateToSecurities: Oas2TranslateFunction<[operationSecurities: unknown], HttpSecurityScheme[][]> = function (operationSecurities) { const securities = getSecurities(this.document, operationSecurities); diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index 7be41c5d..af564d5a 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,26 +1,33 @@ import type { IServer, Optional } from '@stoplight/types'; + +import { withContext } from '../../context'; import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../../guards'; +import { ArrayCallbackParameters } from '../../types'; import { isValidScheme } from '../guards'; import type { Oas2TranslateFunction } from '../types'; -export const translateToServers: Oas2TranslateFunction<[operation: Record], IServer[]> = function ( - operation, -) { - let schemes; - if (Array.isArray(operation.schemes)) { - schemes = operation.schemes; - } else if (Array.isArray(this.document.schemes)) { - schemes = this.document.schemes; - } else { - return []; - } - - return schemes.map(translateToServer, this).filter(isNonNullable); -}; - -export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optional> = function (scheme) { +export const translateToServers = withContext], IServer[]>>( + function (operation) { + let schemes; + if (Array.isArray(operation.schemes)) { + schemes = operation.schemes; + this.context = 'operation'; + } else if (Array.isArray(this.document.schemes)) { + schemes = this.document.schemes; + this.context = 'service'; + } else { + return []; + } + + return schemes.map(translateToServer, this).filter(isNonNullable); + }, +); + +export const translateToServer = withContext< + Oas2TranslateFunction, Optional> +>(function (scheme) { const { host } = this.document; if (typeof host !== 'string' || host.length === 0) { return; @@ -39,8 +46,11 @@ export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optiona uri.pathname = basePath; } + const url = uri.toString().replace(/\/$/, ''); // Remove trailing slash + return { - url: uri.toString().replace(/\/$/, ''), // Remove trailing slash + id: this.generateId(`http_server-${this.parentId}-${url}`), + url, ...pickBy( { @@ -49,4 +59,4 @@ export const translateToServer: Oas2TranslateFunction<[scheme: unknown], Optiona isString, ), }; -}; +}); diff --git a/src/oas3/__fixtures__/id.json b/src/oas3/__fixtures__/id.json new file mode 100644 index 00000000..cd66100f --- /dev/null +++ b/src/oas3/__fixtures__/id.json @@ -0,0 +1,214 @@ +{ + "openapi": "3.1.0", + "x-stoplight-id": "service_abc", + "info": { + "title": "Users API", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:3000" + } + ], + "tags": [ + { + "name": "mutates" + } + ], + "paths": { + "/users/{userId}": { + "parameters": [ + { + "schema": { + "type": "integer" + }, + "name": "userId", + "in": "path", + "required": true, + "description": "Id of an existing user." + }, + { + "$ref": "#/components/parameters/Some-Header" + } + ], + "get": { + "operationId": "get-user", + "summary": "Get User Info by User ID", + "description": "Retrieve the information of the user with the matching user ID.", + "tags": ["tag-without-root-def"], + "parameters": [ + { + "schema": { + "type": "boolean" + }, + "in": "query", + "name": "summaryOnly" + } + ], + "responses": { + "200": { + "description": "User Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "$ref": "#/components/responses/ErrorResponse" + } + } + }, + "post": { + "operationId": "post-users-userId", + "summary": "Create user", + "tags": ["mutates"], + "parameters": [ + { + "schema": { + "type": "integer" + }, + "name": "Post-Specific-Header", + "in": "header" + } + ], + "security": [ + { + "api-key": [] + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + }, + "examples": { + "basic-example": { + "$ref": "#/components/examples/A-Shared-Example" + } + } + } + } + }, + "responses": { + "201": { + "description": "User Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "$ref": "#/components/responses/ErrorResponse" + } + } + } + } + }, + "components": { + "schemas": { + "User": { + "title": "User", + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "address": { + "$ref": "#/components/schemas/Address" + } + }, + "required": ["id"], + "examples": [] + }, + "Address": { + "title": "Address", + "type": "object", + "properties": { + "street": { + "type": "string" + } + }, + "examples": [ + { + "street": "422 W Riverside Drive" + } + ] + }, + "UserId": { + "type": "number", + "title": "UserId", + "minimum": 0 + }, + "Error": { + "title": "Error", + "type": "object", + "properties": { + "code": { + "type": "number" + }, + "msg": { + "type": "string" + } + }, + "required": ["code", "msg"] + } + }, + "parameters": { + "Some-Header": { + "name": "A-Shared-Header", + "in": "header", + "required": false, + "schema": { + "type": "string" + } + } + }, + "examples": { + "A-Shared-Example": { + "value": { + "id": 0, + "address": { + "street": "string" + } + } + } + }, + "securitySchemes": { + "api-key": { + "name": "API Key", + "type": "apiKey", + "in": "query" + } + }, + "responses": { + "ErrorResponse": { + "description": "A generic error response.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + } +} diff --git a/src/oas3/__fixtures__/output.ts b/src/oas3/__fixtures__/output.ts new file mode 100644 index 00000000..ff73d494 --- /dev/null +++ b/src/oas3/__fixtures__/output.ts @@ -0,0 +1,471 @@ +/** + NOTE that if any object anywhere ever has an `x-stoplight-id` on it, prefer that + over calling the generate function. + + Used https://md5calc.com/hash/fnv1a32 to hash the ids. + */ +export default [ + /** + * The http_service + */ + { + // hash(document id - end user needs to be able to customize this..) + // by default could hash("#")? + // this example has a x-stoplight-id prop on the root though, so using that + id: 'service_abc', + version: '1.0', + name: 'Users API', + servers: [ + { + // hash(`http_server-${parentId}-${server.url}`) + // closest parent with an id is the service, so ends up being... + // hash('http_server-service_abc-http://localhost:3000') + id: '98ddf8a4b5bdc', + name: 'Users API', + url: 'http://localhost:3000', + }, + ], + securitySchemes: [ + { + // hash(`http_security-${parentId}-${security.key || security.name}`) + // closest parent with an id is the service, so ends up being... + // hash('http_security-service_abc-api-key') + id: '202a905f9dff6', + key: 'api-key', + type: 'apiKey', + name: 'API Key', + in: 'query', + }, + ], + tags: [ + { + // hash(`tag-${serviceId}-${tag.name}`) + // always generate tags based on the serviceId, so ends up being... + // hash('tag-service_abc-mutates') + id: '936737e88c6fa', + name: 'mutates', + }, + ], + }, + + /** + * http_operation 1 of 2 (the GET operation) + */ + { + // hash(`http_operation-${parentId}-${method}-${pathWithParamNamesEmpty}`) + // for pathWithParamNamesEmpty, remove all characters between {} segments + // closest parent with an id is the service, so ends up being... + // hash('http_operation-service_abc-get-/users/{}') + id: '96043a63b6901', + iid: 'get-user', + description: 'Retrieve the information of the user with the matching user ID.', + method: 'get', + path: '/users/{userId}', + summary: 'Get User Info by User ID', + responses: [ + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // closest parent with an id is the operation, so ends up being... + // hash('http_response-96043a63b6901-200') + id: 'f387e16c7d39d', + code: '200', + description: 'User Found', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-f387e16c7d39d-application/json') + id: 'fce50f391bf57', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de4f083463b7c', + title: 'User', + type: 'object', + properties: { + id: { + type: 'integer', + readOnly: true, + }, + address: { + // @TODO + $ref: '#/components/schemas/Address', + }, + }, + required: ['id'], + examples: [], + }, + examples: [], + encodings: [], + }, + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-f387e16c7d39d-application/xml') + id: '48eeb3ee2a049', + mediaType: 'application/xml', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': '069dfbb6c6315', + type: 'string', + }, + examples: [], + encodings: [], + }, + ], + }, + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // This response was defined in shared components originally.. the closest parent with an + // id is the service, and the key was ErrorResponse so ends up being... + // hash('http_response-service_abc-ErrorResponse') + id: '437771f63f179', + code: '404', + description: 'A generic error response.', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-437771f63f179-application/json') + id: '4d98be34f341a', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': '2691eb0db9efc', + type: 'object', + properties: { + error: { + // @TODO + $ref: '#/components/schemas/Error', + }, + }, + }, + examples: [], + encodings: [], + }, + ], + }, + ], + servers: [ + { + // hash(`http_server-${parentId}-${server.url}`) + // this is coming from the service defined servers (rather than being defined specifically for this one operation) + // so the ID ends up being the same as the service defined one... look in file for "98ddf8a4b5bdc" to find the other def above + id: '98ddf8a4b5bdc', + name: 'Users API', + url: 'http://localhost:3000', + }, + ], + request: { + // Request doesn't need an id + body: { + id: 'd5027559477f8', + // Really this doesn't even need to be here... there is no request body for this op + contents: [], + }, + headers: [ + { + // hash(`http_header-${parentId}-${param.name}`) + // This header was defined in shared components originally, note how this ends up appearing several times in this doc. + // The closest parent with an id is the service, so ends up being... + // hash('http_header-service_abc-A-Shared-Header') + id: '21b1f96bd26ee', + name: 'A-Shared-Header', + required: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'be6b513de1b69', + type: 'string', + }, + examples: [], + }, + ], + query: [ + { + // hash(`http_query-${parentId}-${param.name}`) + // This was defined directly on the operation (not a shared component), so the closest + // parent with an id is the operation, so ends up being... + // hash('http_query-96043a63b6901-summaryOnly') + id: 'efe9534d001fc', + name: 'summaryOnly', + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'boolean', + 'x-stoplight-id': 'aca62504578bd', + }, + examples: [], + }, + ], + cookie: [], + path: [ + { + // hash(`http_path_param-${parentId}-${param.name}`) + // This was defined on the path, so we use the path to generate the id (thus if another operation on this path was in this doc, it would have path param with same id) + // path's id = hash(`http_path-${parentId}-${path}`) + // The closest parent id to a path, is the service, so this equals... (remember that path segments have characters removed, since they are basically meaningless) + // hash('http_path-service_abc-/users/{}') = '05574f79' + // and then the final path param id... + // hash('http_path_param-05574f79-userId') = 'fe171ec8cfd0b' + id: 'fe171ec8cfd0b', + name: 'userId', + required: true, + description: 'Id of an existing user.', + style: 'simple', + schema: { + 'x-stoplight-id': '13ad531bed72e', + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'integer', + }, + examples: [], + }, + ], + }, + tags: [ + { + // hash(`tag-${serviceId}-${tag.name}`) + // always generate tags based on the serviceId, so ends up being... + // hash('tag-service_abc-tag-without-root-def') + id: '9862017e672e6', + name: 'tag-without-root-def', + }, + ], + security: [], + extensions: {}, + }, + + /** + * http_operation 2 of 2 (the POST operation) + */ + { + // Same process as first time... and yes, I know "POST" doesn't make sense on this path lol + id: 'b16a96d287951', + iid: 'post-users-userId', + method: 'post', + path: '/users/{userId}', + summary: 'Create user', + responses: [ + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // closest parent with an id is the operation, so ends up being... + // hash('http_response-b16a96d287951-201') + id: 'd8ca38606ee5d', + code: '201', + description: 'User Created', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-d8ca38606ee5d-application/json') + id: '88460a8f1a612', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de4f083463b7c', + title: 'User', + type: 'object', + properties: { + id: { + type: 'integer', + readOnly: true, + }, + address: { + // @TODO + $ref: '#/components/schemas/Address', + }, + }, + required: ['id'], + examples: [], + }, + examples: [], + encodings: [], + }, + ], + }, + { + // hash(`http_response-${parentId}-${response.code || response key (for shared response)}`) + // This response was defined in shared components originally.. the closest parent with an + // id is the service, and the key was ErrorResponse so ends up being... + // hash('http_response-service_abc-ErrorResponse') + // NOTE how this ID is the same as the 404 response from the get user operation... + id: '437771f63f179', + code: '400', + description: 'A generic error response.', + headers: [], + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-437771f63f179-application/json') + // NOTE how this ID is the same as the 404 response json media type from the get user operation... + id: '4d98be34f341a', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + // hash(`schema-${parentId}-${key}`) + // This schema was defined in shared components originally.. the closest parent with an + // id is the service, and the key was ErrorResponse so ends up being... + // hash('schema-4d98be34f341a-ErrorResponse') + 'x-stoplight-id': '2691eb0db9efc', + type: 'object', + properties: { + error: { + // @TODO + $ref: '#/components/schemas/Error', + }, + }, + }, + examples: [], + encodings: [], + }, + ], + }, + ], + servers: [ + { + // hash(`http_server-${parentId}-${server.url}`) + // this is coming from the service defined servers (rather than being defined specifically for this one operation) + // so the ID ends up being the same as the service defined one (and also present in the get user op)... + // look in file for "98ddf8a4b5bdc" to find the other defs above + id: '98ddf8a4b5bdc', + name: 'Users API', + url: 'http://localhost:3000', + }, + ], + request: { + body: { + // hash(`http_request_body-${parentId}`) + // closest parent with an id is the operation, so ends up being... + // hash('http_request_body-b16a96d287951') + id: '07267ec331fc9', + contents: [ + { + // hash(`http_media-${parentId}-${mediaType}`) + // closest parent with an id is the request body, so ends up being... + // hash('http_media-07267ec331fc9-application/json') + id: '00db77c676e1a', + mediaType: 'application/json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de4f083463b7c', + title: 'User', + type: 'object', + properties: { + id: { + type: 'integer', + readOnly: true, + }, + address: { + // @TODO + $ref: '#/components/schemas/Address', + }, + }, + required: ['id'], + examples: [], + }, + examples: [ + { + // hash(`example-${parentId}-${example key}`) + // This example was defined in shared components originally.. the closest parent with an + // id is the service, and the key was "A-Shared-Example" so ends up being... + // hash('example-service_abc-A-Shared-Example') + id: '5a69041e065b0', + key: 'basic-example', + value: { + id: 0, + address: { + street: 'string', + }, + }, + }, + ], + encodings: [], + }, + ], + }, + headers: [ + { + // hash(`http_header-${parentId}-${param.name}`) + // This was defined directly on the operation (not a shared component), so the closest + // parent with an id is the operation, so ends up being... + // hash('http_header-b16a96d287951-Post-Specific-Header') + id: '1ead595922478', + name: 'Post-Specific-Header', + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'de5897f178a5d', + type: 'integer', + }, + examples: [], + }, + { + // hash(`http_header-${parentId}-${param.name}`) + // This header was defined in shared components originally, note how this ends up appearing several times in this doc. + // The closest parent with an id is the service, so ends up being... + // hash('http_header-service_abc-A-Shared-Header') + id: '21b1f96bd26ee', + name: 'A-Shared-Header', + required: false, + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + 'x-stoplight-id': 'be6b513de1b69', + type: 'string', + }, + examples: [], + }, + ], + query: [], + cookie: [], + path: [ + { + // Same process as other path param, resulting in the same + // ID (so this path param node will end up as single instance in the graph, with an edge from each operation pointing at it) + id: 'fe171ec8cfd0b', + name: 'userId', + required: true, + description: 'Id of an existing user.', + style: 'simple', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + // hash(`http_media-${parentId}-${key}`) + // closest parent with an id is the response, so ends up being... + // hash('http_media-437771f63f179-application/json') + 'x-stoplight-id': '13ad531bed72e', + type: 'integer', + }, + examples: [], + }, + ], + }, + tags: [ + { + // hash(`tag-${serviceId}-${tag.name}`) + // always generate tags based on the serviceId, so ends up being... + // hash('tag-service_abc-mutates') + id: '936737e88c6fa', + name: 'mutates', + }, + ], + security: [ + [ + { + // This is effectively a silly "fake" ref that openapi pulls... so + // we can effectively just re-use the same ID for the relevant securityScheme + // from the root.. note the ID is the same as the root securityScheme id + id: '202a905f9dff6', + key: 'api-key', + type: 'apiKey', + name: 'API Key', + in: 'query', + }, + ], + ], + extensions: {}, + }, +]; diff --git a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap index 92aa3bb3..11c00f35 100644 --- a/src/oas3/__tests__/__snapshots__/operation.test.ts.snap +++ b/src/oas3/__tests__/__snapshots__/operation.test.ts.snap @@ -6,13 +6,14 @@ Object { Object { "callbackName": "myCallback", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "cbId", "method": "post", "path": "http://example.com?transactionId={$request.body#/id}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -26,7 +27,7 @@ Object { }, ], "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", "method": "post", "path": "/subscribe", @@ -36,6 +37,7 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "mediaType": "application/json", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", @@ -45,9 +47,11 @@ Object { }, }, "type": "object", + "x-stoplight-id": Any, }, }, ], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -66,13 +70,14 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -92,13 +97,14 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -109,6 +115,7 @@ Object { "security": Array [], "servers": Array [ Object { + "id": Any, "name": "title", "url": "operation/server", }, @@ -123,13 +130,14 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -140,6 +148,7 @@ Object { "security": Array [], "servers": Array [ Object { + "id": Any, "name": "title", "url": "path/server", }, @@ -154,13 +163,14 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -173,6 +183,7 @@ Object { "summary": "summary", "tags": Array [ Object { + "id": Any, "name": "tag1", }, ], @@ -184,13 +195,14 @@ Object { "deprecated": true, "description": "descr", "extensions": Object {}, - "id": "?http-operation-id?", + "id": Any, "iid": "opid", "method": "get", "path": "/users/{userId}", "request": Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -201,6 +213,7 @@ Object { "security": Array [], "servers": Array [ Object { + "id": Any, "name": "title", "url": "spec/server", }, diff --git a/src/oas3/__tests__/accessors.test.ts b/src/oas3/__tests__/accessors.test.ts index 5ccb31d5..4b0e7249 100644 --- a/src/oas3/__tests__/accessors.test.ts +++ b/src/oas3/__tests__/accessors.test.ts @@ -1,4 +1,10 @@ -import { getSecurities } from '../accessors'; +import { DeepPartial } from '@stoplight/types'; +import { OpenAPIObject } from 'openapi3-ts'; + +import { getSecurities as _getSecurities, OperationSecurities } from '../accessors'; + +const getSecurities = (document: DeepPartial, operationSecurities?: OperationSecurities) => + _getSecurities(document, operationSecurities); describe('getOas3Securities', () => { it('given no global securities should return empty array', () => { @@ -33,10 +39,12 @@ describe('getOas3Securities', () => { }), ).toStrictEqual([ [ - { - type: 'apiKey', - key: 'operationScheme', - }, + [ + 'operationScheme', + { + type: 'apiKey', + }, + ], ], ]); }); @@ -57,10 +65,12 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([ [ - { - type: 'apiKey', - key: 'specScheme', - }, + [ + 'specScheme', + { + type: 'apiKey', + }, + ], ], ]); }); @@ -85,10 +95,12 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([ [ - { - type: 'apiKey', - key: 'specScheme', - }, + [ + 'specScheme', + { + type: 'apiKey', + }, + ], ], ]); }); @@ -113,62 +125,4 @@ describe('getOas3Securities', () => { ), ).toStrictEqual([[]]); }); - - it('should return security for each scope', () => { - const res = getSecurities( - { - components: { - securitySchemes: { - authWith2Scopes: { - type: 'oauth2', - flows: { - authorizationCode: { - scopes: { - accessToken: 'accessToken description', - secScope: 'secScope description', - }, - }, - }, - }, - }, - }, - security: [ - { - authWith2Scopes: ['accessToken', 'secScope'], - }, - ], - }, - [ - { - authWith2Scopes: ['accessToken'], - }, - { authWith2Scopes: ['secScope'] }, - ], - ); - - expect(res).toStrictEqual([ - [ - { - type: 'oauth2', - flows: { - authorizationCode: { - scopes: { accessToken: 'accessToken description' }, - }, - }, - key: 'authWith2Scopes', - }, - ], - [ - { - type: 'oauth2', - flows: { - authorizationCode: { - scopes: { secScope: 'secScope description' }, - }, - }, - key: 'authWith2Scopes', - }, - ], - ]); - }); }); diff --git a/src/oas3/__tests__/ids.test.ts b/src/oas3/__tests__/ids.test.ts new file mode 100644 index 00000000..7aa3126b --- /dev/null +++ b/src/oas3/__tests__/ids.test.ts @@ -0,0 +1,12 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { transformOas3Operations } from '../operation'; +import { transformOas3Service } from '../service'; + +test('should generate proper ids', async () => { + const document = JSON.parse(await fs.promises.readFile(path.join(__dirname, '../__fixtures__/id.json'), 'utf8')); + const { default: output } = await import('../__fixtures__/output'); + + expect([transformOas3Service({ document }), ...transformOas3Operations(document)]).toEqual(output); +}); diff --git a/src/oas3/__tests__/operation.test.ts b/src/oas3/__tests__/operation.test.ts index 6219e040..651601cd 100644 --- a/src/oas3/__tests__/operation.test.ts +++ b/src/oas3/__tests__/operation.test.ts @@ -1,6 +1,13 @@ import { OpenAPIObject } from 'openapi3-ts'; -import { transformOas3Operation, transformOas3Operations } from '../operation'; +import { + transformOas3Operation as _transformOas3Operation, + transformOas3Operations as _transformOas3Operations, +} from '../operation'; + +const transformOas3Operation: typeof _transformOas3Operation = opts => _transformOas3Operation(opts); + +const transformOas3Operations: typeof _transformOas3Operations = opts => _transformOas3Operations(opts); describe('transformOas3Operation', () => { it('should return deprecated property in http operation root', () => { @@ -85,7 +92,7 @@ describe('transformOas3Operation', () => { }, }; - expect(transformOas3Operations(document)).toStrictEqual([ + expect(transformOas3Operations(document)).toEqual([ expect.objectContaining({ path: '/users/{userId}', method: 'get', @@ -98,8 +105,7 @@ describe('transformOas3Operation', () => { internal: false, extensions: {}, }), - { - id: '?http-operation-id?', + expect.objectContaining({ path: '/users/{userId}', method: 'put', request: expect.any(Object), @@ -108,7 +114,7 @@ describe('transformOas3Operation', () => { servers: expect.any(Array), tags: [], extensions: {}, - }, + }), ]); }); @@ -138,7 +144,14 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + }); }); it('given some tags should translate operation with those tags', () => { @@ -174,7 +187,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + tags: [ + { + id: expect.any(String), + }, + ], + }); }); it('given invalid tags should translate operation as there were no tags specified', () => { @@ -236,12 +261,15 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('tags', [ { + id: expect.any(String), name: '2', }, { + id: expect.any(String), name: 'test', }, { + id: expect.any(String), name: 'false', }, ]); @@ -274,7 +302,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + servers: [ + { + id: expect.any(String), + }, + ], + }); }); it.each([2, '', null, [null]])( @@ -339,6 +379,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { + id: expect.any(String), name: 'title', url: 'operation/server', }, @@ -372,7 +413,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + servers: [ + { + id: expect.any(String), + }, + ], + }); }); it.each([2, '', null, [null]])( @@ -437,6 +490,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { + id: expect.any(String), name: 'title', url: 'path/server', }, @@ -470,7 +524,19 @@ describe('transformOas3Operation', () => { method: 'get', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + servers: [ + { + id: expect.any(String), + }, + ], + }); }); it.each([2, '', null, [null]])( @@ -535,6 +601,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('servers', [ { + id: expect.any(String), name: 'title', url: 'spec/server', }, @@ -588,7 +655,32 @@ describe('transformOas3Operation', () => { method: 'post', document, }), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + request: { + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + }, + callbacks: [ + { + id: expect.any(String), + request: { + body: { + id: expect.any(String), + }, + }, + }, + ], + }); }); it('given malformed parameters should translate operation with those parameters', () => { @@ -625,18 +717,21 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/users/{userId}', request: { body: { + id: expect.any(String), contents: [], }, cookie: [], headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'test', }, @@ -644,6 +739,7 @@ describe('transformOas3Operation', () => { name: 'name', style: 'simple', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', format: 'int32', @@ -703,18 +799,21 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/users/{userId}', request: { body: { + id: expect.any(String), contents: [], }, cookie: [], headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'some example', }, @@ -722,6 +821,7 @@ describe('transformOas3Operation', () => { style: 'simple', name: 'name', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'string', format: 'int32', @@ -744,7 +844,6 @@ describe('transformOas3Operation', () => { it('should keep the server variables', () => { const document: Partial = { - id: '?http-service-id?', paths: { '/pets': { get: { @@ -784,6 +883,7 @@ describe('transformOas3Operation', () => { expect(transformOas3Operation({ document, path: '/pets', method: 'get' }).servers).toEqual([ { + id: expect.any(String), description: 'Sample Petstore Server Https', url: 'https://petstore.swagger.io/v2', variables: { @@ -801,6 +901,7 @@ describe('transformOas3Operation', () => { }, }, { + id: expect.any(String), description: 'Sample Petstore Server Http', url: 'http://petstore.swagger.io/v2', }, @@ -887,16 +988,19 @@ describe('transformOas3Operation', () => { document, }), ).toStrictEqual({ - id: '?http-operation-id?', + id: expect.any(String), method: 'get', path: '/pet', request: { body: { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [ { + id: expect.any(String), key: 'pet-shared', value: { type: 'object', @@ -909,6 +1013,7 @@ describe('transformOas3Operation', () => { }, }, { + id: expect.any(String), key: 'pet-not-shared', value: { 'not-shared': true, @@ -917,6 +1022,7 @@ describe('transformOas3Operation', () => { ], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: {}, @@ -931,12 +1037,15 @@ describe('transformOas3Operation', () => { }, responses: [ { + id: expect.any(String), code: '200', contents: [ { + id: expect.any(String), encodings: [], examples: [ { + id: expect.any(String), key: 'pet-shared', value: { properties: { @@ -949,6 +1058,7 @@ describe('transformOas3Operation', () => { }, }, { + id: expect.any(String), key: 'pet-not-shared', value: { 'not-shared': true, @@ -957,6 +1067,7 @@ describe('transformOas3Operation', () => { ], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: {}, @@ -1014,12 +1125,15 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -1076,12 +1190,15 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { @@ -1124,9 +1241,12 @@ describe('transformOas3Operation', () => { method: 'post', document, }), - ).toHaveProperty('request.body', { - contents: [], - }); + ).toHaveProperty( + 'request.body', + expect.objectContaining({ + contents: [], + }), + ); }); describe('OAS 3.1 support', () => { @@ -1207,22 +1327,20 @@ describe('transformOas3Operation', () => { ).toEqual( expect.objectContaining({ responses: [ - { + expect.objectContaining({ code: '200', contents: [ - { - encodings: [], - examples: [], - mediaType: 'application/json', + expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, - }, + }), ], headers: [], - }, + }), ], }), ); @@ -1236,21 +1354,23 @@ describe('transformOas3Operation', () => { ).toEqual( expect.objectContaining({ request: { - body: { + body: expect.objectContaining({ contents: [ expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, }), ], - }, + }), cookie: [], headers: [ expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'string', format: 'email', @@ -1262,19 +1382,19 @@ describe('transformOas3Operation', () => { query: [], }, responses: [ - { + expect.objectContaining({ code: '200', contents: [ expect.objectContaining({ schema: { + 'x-stoplight-id': expect.any(String), $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: {}, }, }), ], - headers: [], - }, + }), ], }), ); @@ -1318,6 +1438,7 @@ describe('transformOas3Operation', () => { }), ).toHaveProperty('responses', [ { + id: expect.any(String), code: '200', contents: expect.any(Array), headers: expect.any(Array), @@ -1327,6 +1448,7 @@ describe('transformOas3Operation', () => { it('should support requestBodies on any method', () => { const document: Partial = { + 'x-stoplight-id': 'abc-def', openapi: '3.1.0', paths: { '/subscribe': { @@ -1359,12 +1481,15 @@ describe('transformOas3Operation', () => { document, }), ).toHaveProperty('request.body', { + id: expect.any(String), contents: [ { + id: expect.any(String), encodings: [], examples: [], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { diff --git a/src/oas3/__tests__/service.test.ts b/src/oas3/__tests__/service.test.ts index 523ac541..06eb125f 100644 --- a/src/oas3/__tests__/service.test.ts +++ b/src/oas3/__tests__/service.test.ts @@ -1,6 +1,15 @@ import { OpenAPIObject } from 'openapi3-ts'; -import { transformOas3Service } from '../service'; +import { transformOas3Service as _transformOas3Service } from '../service'; + +const transformOas3Service: typeof _transformOas3Service = ({ document, ...opts }) => + _transformOas3Service({ + document: { + 'x-stoplight-id': 'abc', + ...document, + }, + ...opts, + }); describe('oas3 service', () => { it('should handle non object security objects', () => { @@ -16,11 +25,12 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', securitySchemes: [ { + id: expect.any(String), key: 't1', type: 'apiKey', in: undefined, @@ -68,11 +78,12 @@ describe('oas3 service', () => { }, ])('should handle lacking flows for oauth2 security object', document => { expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', securitySchemes: [ { + id: expect.any(String), flows: {}, key: 't1', type: 'oauth2', @@ -87,7 +98,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', }); @@ -110,15 +121,17 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', servers: [ { + id: expect.any(String), name: '', url: 'https://petstore.swagger.io/v2', }, { + id: expect.any(String), name: '', url: 'http://petstore.swagger.io/v2', }, @@ -135,7 +148,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', }); @@ -143,7 +156,7 @@ describe('oas3 service', () => { it('should handle server variables', () => { const document: Partial = { - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', servers: [ @@ -172,11 +185,12 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '', servers: [ { + id: expect.any(String), description: 'Sample Petstore Server Https', url: 'https://petstore.swagger.io/v2', variables: { @@ -194,6 +208,7 @@ describe('oas3 service', () => { }, }, { + id: expect.any(String), description: 'Sample Petstore Server Http', url: 'http://petstore.swagger.io/v2', }, @@ -240,7 +255,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', summary: 'Very cool API', @@ -260,7 +275,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toEqual({ - id: '?http-service-id?', + id: 'abc', name: '', version: '1.0', license: { @@ -286,7 +301,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', version: '1.0.0', logo: { @@ -310,7 +325,7 @@ describe('oas3 service', () => { }; expect(transformOas3Service({ document })).toStrictEqual({ - id: '?http-service-id?', + id: 'abc', name: 'no-title', contact: { url: 'https://stoplight.io', diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index 38f66cf5..79ea47bb 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,46 +1,50 @@ import { isPlainObject } from '@stoplight/json'; -import type { DeepPartial, Dictionary, HttpSecurityScheme } from '@stoplight/types'; +import type { DeepPartial, Dictionary, HttpSecurityScheme, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); import type { OpenAPIObject } from 'openapi3-ts'; +import { isNonNullable } from '../guards'; import { entries } from '../utils'; -import { isSecurityScheme, isSecuritySchemeWithKey } from './guards'; +import { isSecurityScheme } from './guards'; export type OperationSecurities = Dictionary[] | undefined; -export type SecurityWithKey = HttpSecurityScheme & { key: string }; export function getSecurities( document: DeepPartial, operationSecurities?: unknown, -): SecurityWithKey[][] { +): [key: string, security: Omit][][] { const definitions = document.components?.securitySchemes; if (!isPlainObject(definitions)) return []; return (Array.isArray(operationSecurities) ? operationSecurities : document.security || []).map(operationSecurity => { return entries(operationSecurity) - .map(([opScheme, scopes]) => { + .map]>>(([opScheme, scopes]) => { const definition = definitions[opScheme]; - if (isSecurityScheme(definition) && definition.type === 'oauth2') { + if (!isSecurityScheme(definition)) return; + + if (definition.type === 'oauth2') { // Put back only the flows that are part of the current definition - return { - ...definition, - flows: Object.fromEntries( - entries(definition.flows).map(([name, flow]) => [ - name, - { - ...flow, - scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), - }, - ]), - ), - key: opScheme, - }; + return [ + opScheme, + { + ...definition, + flows: Object.fromEntries( + entries(definition.flows).map(([name, flow]) => [ + name, + { + ...flow, + scopes: pickBy(flow?.scopes, (_val, key) => scopes?.includes(key)), + }, + ]), + ), + }, + ]; } - return { ...definition, key: opScheme }; + return [opScheme, definition]; }) - .filter(isSecuritySchemeWithKey); + .filter(isNonNullable); }); } diff --git a/src/oas3/guards.ts b/src/oas3/guards.ts index 3bac2280..4c964fab 100644 --- a/src/oas3/guards.ts +++ b/src/oas3/guards.ts @@ -11,14 +11,9 @@ import type { TagObject, } from 'openapi3-ts'; -import type { SecurityWithKey } from './accessors'; - export const isSecurityScheme = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecuritySchemeObject => isPlainObject(maybeSecurityScheme) && typeof maybeSecurityScheme.type === 'string'; -export const isSecuritySchemeWithKey = (maybeSecurityScheme: unknown): maybeSecurityScheme is SecurityWithKey => - isSecurityScheme(maybeSecurityScheme) && typeof maybeSecurityScheme.key === 'string'; - export const isBaseParameterObject = ( maybeBaseParameterObject: unknown, ): maybeBaseParameterObject is BaseParameterObject => diff --git a/src/oas3/index.ts b/src/oas3/index.ts index 2e002f33..f2e98a7a 100644 --- a/src/oas3/index.ts +++ b/src/oas3/index.ts @@ -1,3 +1,2 @@ export * from './operation'; export * from './service'; -export * from './transformers/request'; diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index f423a26c..ca08e7ca 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,54 +1,42 @@ -import { IHttpOperation } from '@stoplight/types'; -import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; +import type { DeepPartial, IHttpOperation } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import type { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../context'; import { isNonNullable } from '../guards'; -import { transformOasOperations } from '../oas'; -import { getExtensions } from '../oas/accessors'; -import { translateToTags } from '../oas/tags'; +import { transformOasOperation, transformOasOperations } from '../oas'; +import { resolveRef } from '../oas/resolver'; import type { Oas3HttpOperationTransformer } from '../oas/types'; -import { maybeResolveLocalRef } from '../utils'; +import { Fragment } from '../types'; import { translateToCallbacks } from './transformers/callbacks'; import { translateToRequest } from './transformers/request'; import { translateToResponses } from './transformers/responses'; import { translateToSecurities } from './transformers/securities'; import { translateToServers } from './transformers/servers'; -export function transformOas3Operations(document: OpenAPIObject): IHttpOperation[] { +export function transformOas3Operations(document: DeepPartial): IHttpOperation[] { return transformOasOperations(document, transformOas3Operation); } -export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document, path, method }) => { - const pathObj = maybeResolveLocalRef(document, document?.paths?.[path]) as PathsObject; - if (typeof pathObj !== 'object' || pathObj === null) { - throw new Error(`Could not find ${['paths', path].join('/')} in the provided spec.`); - } +export const transformOas3Operation: Oas3HttpOperationTransformer = ({ document: _document, path, method }) => { + const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); + const httpOperation = transformOasOperation.call(ctx, path, method); + const pathObj = ctx.maybeResolveLocalRef(ctx.document.paths![path]) as Fragment; + const operation = ctx.maybeResolveLocalRef(pathObj[method]) as Fragment; - const operation = maybeResolveLocalRef(document, pathObj[method]) as OperationObject; - if (!operation) { - throw new Error(`Could not find ${['paths', path, method].join('/')} in the provided spec.`); - } + return { + ...httpOperation, - const ctx = createContext(document); - - const httpOperation: IHttpOperation = { - id: '?http-operation-id?', - iid: operation.operationId, - description: operation.description, - deprecated: operation.deprecated, - internal: operation['x-internal'], - method, - path, - summary: operation.summary, responses: translateToResponses.call(ctx, operation.responses), request: translateToRequest.call(ctx, pathObj, operation), - callbacks: translateToCallbacks.call(ctx, operation.callbacks), - tags: translateToTags.call(ctx, operation.tags), security: translateToSecurities.call(ctx, operation.security), - extensions: getExtensions(operation), servers: translateToServers.call(ctx, pathObj, operation), - }; - return pickBy(httpOperation, isNonNullable) as IHttpOperation; + ...pickBy( + { + callbacks: translateToCallbacks.call(ctx, operation.callbacks), + }, + isNonNullable, + ), + }; }; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index acce098f..984418ad 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,20 +1,22 @@ import { isPlainObject } from '@stoplight/json'; -import type { Optional } from '@stoplight/types'; +import type { HttpSecurityScheme, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); -import { createContext } from '../context'; + +import { createContext, DEFAULT_ID_GENERATOR, withContext } from '../context'; import { isNonNullable } from '../guards'; +import { resolveRef } from '../oas/resolver'; import { transformOasService } from '../oas/service'; import type { Oas3HttpServiceTransformer } from '../oas/types'; import { ArrayCallbackParameters } from '../types'; import { entries } from '../utils'; -import { SecurityWithKey } from './accessors'; import { isSecurityScheme } from './guards'; import { translateToSingleSecurity } from './transformers/securities'; import { translateToServer } from './transformers/servers'; import { Oas3TranslateFunction } from './types'; -export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) => { - const ctx = createContext(document); +export const transformOas3Service: Oas3HttpServiceTransformer = ({ document: _document }) => { + const ctx = createContext(_document, resolveRef, DEFAULT_ID_GENERATOR); + const { document } = ctx; const httpService = transformOasService.call(ctx); if (typeof document.info?.summary === 'string') { @@ -84,16 +86,10 @@ export const transformOas3Service: Oas3HttpServiceTransformer = ({ document }) = return httpService; }; -const translateSecurityScheme: Oas3TranslateFunction< - ArrayCallbackParameters<[name: string, scheme: unknown]>, - Optional -> = function ([key, definition]) { +const translateSecurityScheme = withContext< + Oas3TranslateFunction, Optional> +>(function ([key, definition]) { if (!isSecurityScheme(definition)) return; - const transformed = translateToSingleSecurity.call(this, definition); - if (transformed && 'key' in transformed) { - transformed.key = key; - } - - return transformed; -}; + return translateToSingleSecurity.call(this, [key, definition]); +}); diff --git a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap index d45eea44..207ca782 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/content.test.ts.snap @@ -10,20 +10,24 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "a", "summary": "example summary", "value": "hey", }, Object { + "id": Any, "key": "__default", "value": Object {}, }, ], "explode": true, + "id": Any, "name": "header-name", "required": true, "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, "style": "simple", } @@ -42,11 +46,13 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "a", "summary": "example summary", "value": "hey", }, ], + "id": Any, "name": "h1", "style": "simple", }, @@ -58,14 +64,17 @@ Object { ], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -80,6 +89,7 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "name": "h1", "style": "simple", }, @@ -95,6 +105,7 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "name": "h1", "style": "simple", }, @@ -106,14 +117,17 @@ Object { ], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -123,14 +137,17 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -140,13 +157,16 @@ Object { "encodings": Array [], "examples": Array [ Object { + "id": Any, "key": "default", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; @@ -163,14 +183,17 @@ Object { ], "examples": Array [ Object { + "id": Any, "key": "example", "summary": "multi example", "value": "hey", }, ], + "id": Any, "mediaType": "mediaType", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, }, } `; diff --git a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap index a987deb6..598086ab 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -7,14 +7,17 @@ Object { Object { "encodings": Array [], "examples": Array [], + "id": Any, "mediaType": "content-a", "schema": Object { "$schema": "http://json-schema.org/draft-07/schema#", "deprecated": true, + "x-stoplight-id": Any, }, }, ], "description": "descr", + "id": Any, "required": true, }, "cookie": Array [], @@ -28,6 +31,7 @@ exports[`translateOas3ToRequest given no request body should translate parameter Object { "body": Object { "contents": Array [], + "id": Any, }, "cookie": Array [], "headers": Array [ @@ -39,6 +43,7 @@ Object { }, "description": "descr", "examples": Array [], + "id": Any, "name": "param-name-3", "style": "simple", }, @@ -53,6 +58,7 @@ Object { }, "description": "descr", "examples": Array [], + "id": Any, "name": "param-name-2", "style": "simple", }, @@ -65,6 +71,7 @@ Object { "deprecated": true, "description": "descr", "examples": Array [], + "id": Any, "name": "param-name-1", "style": "simple", }, diff --git a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap index ef2bed75..49ed0119 100644 --- a/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap +++ b/src/oas3/transformers/__tests__/__snapshots__/responses.test.ts.snap @@ -1,82 +1,95 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`translateToOas3Responses given a response in dictionary should translate 1`] = ` -Array [ - Object { - "code": "200", - "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [ - Object { - "key": "default", - "value": "dumb", - }, - ], - "mediaType": "fake-content-type-200", - }, - ], - "description": "descr 200", - "headers": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "name": "fake-header-name-200", - "style": "simple", - }, - ], - }, - Object { - "code": "default", - "contents": Array [ - Object { - "encodings": Array [], - "examples": Array [], - "mediaType": "fake-content-type", - }, - ], - "description": "descr", - "headers": Array [ - Object { - "description": "calls per hour allowed by the user", - "encodings": Array [], - "examples": Array [ - Object { - "key": "__default", - "value": 1000, - }, - ], - "name": "fake-header-name-1", - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": "integer", +Object { + "code": "200", + "contents": Array [ + Object { + "encodings": Array [], + "examples": Array [ + Object { + "id": Any, + "key": "default", + "value": "dumb", + }, + ], + "id": Any, + "mediaType": "fake-content-type-200", + }, + ], + "description": "descr 200", + "headers": Array [ + Object { + "encodings": Array [], + "examples": Array [], + "id": Any, + "name": "fake-header-name-200", + "style": "simple", + }, + ], + "id": Any, +} +`; + +exports[`translateToOas3Responses given a response in dictionary should translate 2`] = ` +Object { + "code": "default", + "contents": Array [ + Object { + "encodings": Array [], + "examples": Array [], + "id": Any, + "mediaType": "fake-content-type", + }, + ], + "description": "descr", + "headers": Array [ + Object { + "description": "calls per hour allowed by the user", + "encodings": Array [], + "examples": Array [ + Object { + "id": Any, + "key": "__default", + "value": 1000, }, - "style": "simple", + ], + "id": Any, + "name": "fake-header-name-1", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer", + "x-stoplight-id": Any, }, - Object { - "description": "calls per hour allowed by the user", - "encodings": Array [], - "examples": Array [ - Object { - "key": "__default", - "value": 1000, - }, - ], - "name": "fake-header-name-2", - "required": true, - "schema": Object { - "$schema": "http://json-schema.org/draft-07/schema#", - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": "integer", + "style": "simple", + }, + Object { + "description": "calls per hour allowed by the user", + "encodings": Array [], + "examples": Array [ + Object { + "id": Any, + "key": "__default", + "value": 1000, }, - "style": "simple", + ], + "id": Any, + "name": "fake-header-name-2", + "required": true, + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "format": "int32", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer", + "x-stoplight-id": Any, }, - ], - }, -] + "style": "simple", + }, + ], + "id": Any, +} `; diff --git a/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap b/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap deleted file mode 100644 index 94ef254f..00000000 --- a/src/oas3/transformers/__tests__/__snapshots__/servers.test.ts.snap +++ /dev/null @@ -1,30 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`translateToServers translate single ServerObject to IServer 1`] = ` -Array [ - Object { - "description": "description", - "url": "http://stoplight.io/path", - "variables": Object { - "a": Object { - "default": "false", - "description": "a - descr", - "enum": Array [ - "false", - "true", - "false", - ], - }, - "b": Object { - "default": "123", - "description": "b - descr", - "enum": Array [ - "1", - "2", - "3", - ], - }, - }, - }, -] -`; diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index d22bf44a..47017ebd 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -1,16 +1,17 @@ import type { SchemaObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateHeaderObject as _translateHeaderObject, translateMediaTypeObject as _translateMediaTypeObject, } from '../content'; const translateMediaTypeObject = (document: any, object: unknown, key: string) => - _translateMediaTypeObject.call(createContext(document), [key, object], 0, []); + _translateMediaTypeObject.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); const translateHeaderObject = (object: unknown, key: string) => - _translateHeaderObject.call(createContext({}), [key, object], 0, []); + _translateHeaderObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); describe('translateMediaTypeObject', () => { afterEach(() => { @@ -23,6 +24,7 @@ describe('translateMediaTypeObject', () => { it('given empty object, should return nothing', () => { expect(translateMediaTypeObject({}, {}, 'mediaType')).toStrictEqual({ + id: expect.any(String), encodings: [], examples: [], mediaType: 'mediaType', @@ -39,6 +41,7 @@ describe('translateMediaTypeObject', () => { 'mediaType', ), ).toStrictEqual({ + id: expect.any(String), encodings: [], examples: [], mediaType: 'mediaType', @@ -56,7 +59,17 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given multiple examples should translate to IHttpContent', () => { @@ -70,7 +83,17 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given encodings should translate each encoding to array item', () => { @@ -109,7 +132,33 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + encodings: [ + { + headers: [ + { + id: expect.any(String), + }, + ], + }, + { + headers: [ + { + id: expect.any(String), + }, + ], + }, + ], + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given complex nested media type object should translate correctly', () => { @@ -143,7 +192,31 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + encodings: [ + { + headers: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + }, + ], + }, + ], + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('should skip nullish headers', () => { @@ -165,7 +238,17 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('given encoding with no style it should not throw an error', () => { @@ -257,7 +340,7 @@ describe('translateMediaTypeObject', () => { }, 'mediaType', ), - ).toHaveProperty('examples', [{ key: 'default', value: defaultExample }]); + ).toHaveProperty('examples', [{ id: expect.any(String), key: 'default', value: defaultExample }]); }); }); @@ -279,8 +362,8 @@ describe('translateMediaTypeObject', () => { 'mediaType', ), ).toHaveProperty('examples', [ - { key: 'default', value: { name: 'root default value' } }, - { key: 'example', value: { name: 'root example value' } }, + { id: expect.any(String), key: 'default', value: { name: 'root default value' } }, + { id: expect.any(String), key: 'example', value: { name: 'root example value' } }, ]); }); }); @@ -377,7 +460,20 @@ describe('translateHeaderObject', () => { }, 'header-name', ), - ).toMatchSnapshot(); + ).toMatchSnapshot({ + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }); }); it('should handle nullish value gracefully', () => { diff --git a/src/oas3/transformers/__tests__/request.test.ts b/src/oas3/transformers/__tests__/request.test.ts index 94b8f51c..fa13a427 100644 --- a/src/oas3/transformers/__tests__/request.test.ts +++ b/src/oas3/transformers/__tests__/request.test.ts @@ -1,10 +1,9 @@ -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToRequest as _translateToRequest } from '../request'; -const translateToRequest = (path: Record, operation: Record) => { - const ctx = createContext({ paths: { '/api': path } }); - return _translateToRequest.call(ctx, path, operation); -}; +const translateToRequest = (path: Record, operation: Record) => + _translateToRequest.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), path, operation); describe('translateOas3ToRequest', () => { it('given no request body should translate parameters', () => { @@ -50,7 +49,24 @@ describe('translateOas3ToRequest', () => { get: operation, }; - expect(translateToRequest(path, operation)).toMatchSnapshot(); + expect(translateToRequest(path, operation)).toMatchSnapshot({ + body: { + id: expect.any(String), + }, + headers: [ + { + id: expect.any(String), + }, + ], + query: [ + { + id: expect.any(String), + }, + { + id: expect.any(String), + }, + ], + }); }); it('give a request body should translate it', () => { @@ -72,6 +88,18 @@ describe('translateOas3ToRequest', () => { post: operation, }; - expect(translateToRequest(path, operation)).toMatchSnapshot(); + expect(translateToRequest(path, operation)).toMatchSnapshot({ + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + }); }); }); diff --git a/src/oas3/transformers/__tests__/responses.test.ts b/src/oas3/transformers/__tests__/responses.test.ts index fe04ccc0..934851de 100644 --- a/src/oas3/transformers/__tests__/responses.test.ts +++ b/src/oas3/transformers/__tests__/responses.test.ts @@ -1,11 +1,12 @@ import type { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToResponses as _translateToResponses } from '../responses'; const translateToResponses = (document: DeepPartial, responses: unknown) => - _translateToResponses.call(createContext(document), responses); + _translateToResponses.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), responses); describe('translateToOas3Responses', () => { it('given empty dictionary should return empty array', () => { @@ -13,49 +14,99 @@ describe('translateToOas3Responses', () => { }); it('given a response in dictionary should translate', () => { - expect( - translateToResponses( - {}, - { - default: { - content: { - 'fake-content-type': {}, - }, - description: 'descr', - headers: { - 'fake-header-name-1': { - description: 'calls per hour allowed by the user', - schema: { - type: 'integer', - format: 'int32', - }, - example: 1000, + const responses = translateToResponses( + {}, + { + default: { + content: { + 'fake-content-type': {}, + }, + description: 'descr', + headers: { + 'fake-header-name-1': { + description: 'calls per hour allowed by the user', + schema: { + type: 'integer', + format: 'int32', }, - 'fake-header-name-2': { - description: 'calls per hour allowed by the user', - schema: { - type: 'integer', - format: 'int32', - }, - required: true, - example: 1000, + example: 1000, + }, + 'fake-header-name-2': { + description: 'calls per hour allowed by the user', + schema: { + type: 'integer', + format: 'int32', }, + required: true, + example: 1000, }, }, - 200: { - content: { - 'fake-content-type-200': { - example: 'dumb', - }, + }, + 200: { + content: { + 'fake-content-type-200': { + example: 'dumb', }, - description: 'descr 200', - headers: { - 'fake-header-name-200': {}, + }, + description: 'descr 200', + headers: { + 'fake-header-name-200': {}, + }, + }, + }, + ); + + expect(responses).toHaveLength(2); + expect(responses[0]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), }, + ], + }, + ], + headers: [ + { + id: expect.any(String), + }, + ], + }); + expect(responses[1]).toMatchSnapshot({ + id: expect.any(String), + contents: [ + { + id: expect.any(String), + }, + ], + headers: [ + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), }, }, - ), - ).toMatchSnapshot(); + { + id: expect.any(String), + examples: [ + { + id: expect.any(String), + }, + ], + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given a response with nullish headers in dictionary should translate', () => { @@ -72,6 +123,7 @@ describe('translateToOas3Responses', () => { ), ).toStrictEqual([ { + id: expect.any(String), code: '200', contents: [], headers: [], @@ -92,6 +144,7 @@ describe('translateToOas3Responses', () => { ), ).toStrictEqual([ { + id: expect.any(String), code: '201', contents: [], description: 'description 201', @@ -149,12 +202,15 @@ describe('translateToOas3Responses', () => { expect(translateToResponses(document, document.paths!['/user'].get.responses)).toEqual([ { + id: expect.any(String), code: '200', contents: [ { + id: expect.any(String), encodings: [], examples: [ { + id: expect.any(String), key: 'my-example', value: { id: 1, @@ -163,6 +219,7 @@ describe('translateToOas3Responses', () => { ], mediaType: 'application/json', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', properties: { id: { @@ -208,13 +265,16 @@ describe('translateToOas3Responses', () => { const expected = [ { + id: expect.any(String), code: '200', contents: [], description: 'OK', headers: [ { + id: expect.any(String), name: 'X-Page', schema: { + 'x-stoplight-id': expect.any(String), $schema: 'http://json-schema.org/draft-07/schema#', type: 'integer', }, @@ -223,6 +283,7 @@ describe('translateToOas3Responses', () => { encodings: [], examples: [ { + id: expect.any(String), key: '__default', value: 3, }, diff --git a/src/oas3/transformers/__tests__/securities.test.ts b/src/oas3/transformers/__tests__/securities.test.ts index b682c291..f24f7c46 100644 --- a/src/oas3/transformers/__tests__/securities.test.ts +++ b/src/oas3/transformers/__tests__/securities.test.ts @@ -1,12 +1,13 @@ import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { OperationSecurities } from '../../accessors'; import { translateToSecurities as _translateToSecurities } from '../securities'; const translateToSecurities = (document: DeepPartial, operationSecurities: OperationSecurities) => - _translateToSecurities.call(createContext(document), operationSecurities); + _translateToSecurities.call(createContext(document, resolveRef, DEFAULT_ID_GENERATOR), operationSecurities); describe('securities', () => { describe('translateToSecurities', () => { @@ -29,6 +30,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'basic-security', type: 'http', description: 'a description', @@ -57,6 +59,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'digest-security', type: 'http', description: 'a description', @@ -86,6 +89,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'bearer-security', type: 'http', description: 'a description', @@ -115,6 +119,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'openIdConnect-security', type: 'openIdConnect', description: 'a description', @@ -144,6 +149,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'header', @@ -173,6 +179,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), key: 'basic-security', type: 'http', description: 'a description', @@ -207,6 +214,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-flow-security', @@ -240,6 +248,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { password: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'password-flow-security', @@ -273,6 +282,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { clientCredentials: { tokenUrl: 'a token url', scopes: { scope: 'value' } } }, key: 'clientCredentials-flow-security', @@ -307,6 +317,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), description: 'a description', flows: { authorizationCode: { @@ -371,6 +382,7 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), type: 'http', description: 'a description', key: 'http-security', @@ -378,6 +390,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', @@ -386,6 +399,7 @@ describe('securities', () => { ], [ { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', @@ -404,17 +418,20 @@ describe('securities', () => { ).toEqual([ [ { + id: expect.any(String), type: 'http', description: 'a description', key: 'http-security', }, { + id: expect.any(String), description: 'a description', flows: { implicit: { authorizationUrl: 'a url', scopes: { scope: 'value' } } }, key: 'implicit-security', type: 'oauth2', }, { + id: expect.any(String), name: 'a name', type: 'apiKey', in: 'query', diff --git a/src/oas3/transformers/__tests__/servers.test.ts b/src/oas3/transformers/__tests__/servers.test.ts index d6573d06..5146df72 100644 --- a/src/oas3/transformers/__tests__/servers.test.ts +++ b/src/oas3/transformers/__tests__/servers.test.ts @@ -1,12 +1,18 @@ import { DeepPartial } from '@stoplight/types'; import { OpenAPIObject } from 'openapi3-ts'; -import { createContext } from '../../../context'; +import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; +import { resolveRef } from '../../../oas/resolver'; import { translateToServers as _translateToServers } from '../servers'; const translateToServers = ( document: DeepPartial & { paths: { '/pet': { get: Record } } }, -) => _translateToServers.call(createContext(document), document.paths['/pet'], document.paths['/pet'].get); +) => + _translateToServers.call( + createContext(document, resolveRef, DEFAULT_ID_GENERATOR), + document.paths['/pet'], + document.paths['/pet'].get, + ); describe('translateToServers', () => { it('translate single ServerObject to IServer', () => { @@ -36,7 +42,25 @@ describe('translateToServers', () => { }, }; - expect(translateToServers(document)).toMatchSnapshot(); + expect(translateToServers(document)).toStrictEqual([ + { + id: expect.any(String), + description: 'description', + url: 'http://stoplight.io/path', + variables: { + a: { + default: 'false', + description: 'a - descr', + enum: ['false', 'true', 'false'], + }, + b: { + default: '123', + description: 'b - descr', + enum: ['1', '2', '3'], + }, + }, + }, + ]); }); it('filters out invalid variables', () => { @@ -68,6 +92,7 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { + id: expect.any(String), description: 'description', url: 'http://stoplight.io/path', variables: { @@ -112,6 +137,7 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { + id: expect.any(String), description: 'description', url: 'http://stoplight.io/pet.get', }, @@ -142,6 +168,7 @@ describe('translateToServers', () => { expect(translateToServers(document)).toStrictEqual([ { + id: expect.any(String), description: 'description', url: 'http://stoplight.io/pet', }, diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index 120d91b4..da16f366 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -1,6 +1,5 @@ import { isPlainObject } from '@stoplight/json'; import { - Dictionary, HttpParamStyles, IHttpEncoding, IHttpHeaderParam, @@ -11,12 +10,15 @@ import { import type { JSONSchema7 } from 'json-schema'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; import { ArrayCallbackParameters, Fragment } from '../../types'; -import { entries, maybeResolveLocalRef } from '../../utils'; +import { entries } from '../../utils'; import { isHeaderObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; +import { translateToExample } from './examples'; const ACCEPTABLE_STYLES: (string | undefined)[] = [ HttpParamStyles.Form, @@ -25,7 +27,7 @@ const ACCEPTABLE_STYLES: (string | undefined)[] = [ HttpParamStyles.DeepObject, ]; -function isAcceptableStyle( +function hasAcceptableStyle( encodingPropertyObject: T, ): encodingPropertyObject is T & { style: @@ -37,12 +39,14 @@ function isAcceptableStyle( return typeof encodingPropertyObject.style === 'string' && ACCEPTABLE_STYLES.includes(encodingPropertyObject.style); } -const translateEncodingPropertyObject: Oas3TranslateFunction< - ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, - Optional -> = function ([property, encodingPropertyObject]) { +const translateEncodingPropertyObject = withContext< + Oas3TranslateFunction< + ArrayCallbackParameters<[property: string, encodingPropertyObject: unknown]>, + Optional + > +>(function ([property, encodingPropertyObject]) { if (!isPlainObject(encodingPropertyObject)) return; - if (!isAcceptableStyle(encodingPropertyObject)) return; + if (!hasAcceptableStyle(encodingPropertyObject)) return; return { property, @@ -64,16 +68,19 @@ const translateEncodingPropertyObject: Oas3TranslateFunction< isString, ), }; -}; +}); -export const translateHeaderObject = < +export const translateHeaderObject = withContext< Oas3TranslateFunction, Optional> ->function ([name, unresolvedHeaderObject]) { +>(function ([name, unresolvedHeaderObject]) { const headerObject = this.maybeResolveLocalRef(unresolvedHeaderObject); if (!isPlainObject(headerObject)) return; + const id = this.generateId(`http_header-${this.parentId}-${name}`); + if (!isHeaderObject(headerObject)) { return { + id, encodings: [], examples: [], name, @@ -86,6 +93,7 @@ export const translateHeaderObject = < const contentValue = isPlainObject(contentObject) ? Object.values(contentObject)[0] : null; const baseContent: IHttpHeaderParam = { + id, name, style: HttpParamStyles.Simple, @@ -108,7 +116,6 @@ export const translateHeaderObject = < { allowEmptyValue: headerObject.allowEmptyValue, allowReserved: headerObject.allowReserved, - explode: headerObject.explode, required: headerObject.required, deprecated: headerObject.deprecated, @@ -128,14 +135,14 @@ export const translateHeaderObject = < } if ('example' in contentValue) { - examples.push(transformDefaultExample.call(this, '__default_content', contentValue.example)); + examples.push(translateToDefaultExample.call(this, '__default_content', contentValue.example)); } } examples.push(...entries(headerObject.examples).map(translateToExample, this).filter(isNonNullable)); if ('example' in headerObject) { - examples.push(transformDefaultExample.call(this, '__default', headerObject.example)); + examples.push(translateToDefaultExample.call(this, '__default', headerObject.example)); } return { @@ -143,34 +150,33 @@ export const translateHeaderObject = < encodings, examples, }; -}; +}); -const translateSchemaMediaTypeObject: Oas3TranslateFunction<[schema: unknown], Optional> = function ( - schema, -) { - if (!isPlainObject(schema)) return; +const translateSchemaMediaTypeObject = withContext>>( + function (schema) { + if (!isPlainObject(schema)) return; - return translateSchemaObject.call(this, schema); -}; + return translateSchemaObject.call(this, schema); + }, +); -export const translateMediaTypeObject: Oas3TranslateFunction< - ArrayCallbackParameters<[mediaType: string, mediaObject: unknown]>, - Optional -> = function ([mediaType, mediaObject]) { +export const translateMediaTypeObject = withContext< + Oas3TranslateFunction, Optional> +>(function ([mediaType, mediaObject]) { if (!isPlainObject(mediaObject)) return; - const resolvedMediaObject = resolveMediaObject(this.document, mediaObject); - const { schema, encoding, examples } = resolvedMediaObject; - + const id = this.generateId(`http_media-${this.parentId}-${mediaType}`); + const { schema, encoding, examples } = mediaObject; const jsonSchema = translateSchemaMediaTypeObject.call(this, schema); - const example = resolvedMediaObject.example || jsonSchema?.examples?.[0]; + const defaultExample = 'example' in mediaObject ? mediaObject.example : jsonSchema?.examples?.[0]; return { + id, mediaType, // Note that I'm assuming all references are resolved examples: [ - example ? transformDefaultExample.call(this, 'default', example) : undefined, + defaultExample !== undefined ? translateToDefaultExample.call(this, 'default', defaultExample) : undefined, ...entries(examples).map(translateToExample, this), ].filter(isNonNullable), encodings: entries(encoding).map(translateEncodingPropertyObject, this).filter(isNonNullable), @@ -182,51 +188,4 @@ export const translateMediaTypeObject: Oas3TranslateFunction< isNonNullable, ), }; -}; - -function resolveMediaObject(document: unknown, maybeMediaObject: Dictionary) { - const mediaObject = { ...maybeMediaObject }; - if (isPlainObject(mediaObject.schema)) { - mediaObject.schema = maybeResolveLocalRef(document, mediaObject.schema); - } - - if (isPlainObject(mediaObject.examples)) { - const examples = { ...mediaObject.examples }; - mediaObject.examples = examples; - for (const [exampleName, exampleValue] of entries(examples)) { - examples[exampleName] = maybeResolveLocalRef(document, exampleValue); - } - } - - return mediaObject; -} - -const transformDefaultExample: Oas3TranslateFunction<[key: string, value: unknown], INodeExample> = function ( - key, - value, -) { - return { - value, - key, - }; -}; - -const translateToExample: Oas3TranslateFunction< - ArrayCallbackParameters<[key: string, example: unknown]>, - Optional -> = function ([key, example]) { - if (!isPlainObject(example)) return; - - return { - value: example.value, - key, - - ...pickBy( - { - summary: example.summary, - description: example.description, - }, - isString, - ), - }; -}; +}); diff --git a/src/oas3/transformers/examples.ts b/src/oas3/transformers/examples.ts new file mode 100644 index 00000000..5bfb8032 --- /dev/null +++ b/src/oas3/transformers/examples.ts @@ -0,0 +1,33 @@ +import { isPlainObject } from '@stoplight/json'; +import { INodeExample, Optional } from '@stoplight/types'; +import pickBy = require('lodash.pickby'); + +import { withContext } from '../../context'; +import { isString } from '../../guards'; +import { getSharedKey } from '../../oas/resolver'; +import type { ArrayCallbackParameters } from '../../types'; +import type { Oas3TranslateFunction } from '../types'; + +export const translateToExample = withContext< + Oas3TranslateFunction, Optional> +>(function ([key, example]) { + const resolvedExample = this.maybeResolveLocalRef(example); + + if (!isPlainObject(resolvedExample)) return; + + const actualKey = this.context === 'service' ? getSharedKey(resolvedExample) : key; + + return { + id: this.generateId(`example-${this.parentId}-${actualKey}`), + value: resolvedExample.value, + key, + + ...pickBy( + { + summary: resolvedExample.summary, + description: resolvedExample.description, + }, + isString, + ), + }; +}); diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index fd68d1ef..11533957 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -4,8 +4,6 @@ import type { IHttpOperationRequest, IHttpOperationRequestBody, IHttpParam, - INodeExample, - INodeExternalExample, Optional, } from '@stoplight/types'; import { HttpParamStyles } from '@stoplight/types'; @@ -13,137 +11,133 @@ import type { JSONSchema7 } from 'json-schema'; import type { ParameterObject } from 'openapi3-ts'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; import { OasVersion } from '../../oas'; -import { getValidOasParameters } from '../../oas/accessors'; +import { createOasParamsIterator } from '../../oas/accessors'; import { isValidParamStyle } from '../../oas/guards'; +import { translateToDefaultExample } from '../../oas/transformers/examples'; import { translateSchemaObject } from '../../oas/transformers/schema'; -import type { ArrayCallbackParameters, Fragment } from '../../types'; import { entries } from '../../utils'; import { isRequestBodyObject } from '../guards'; -import type { Oas3TranslateFunction } from '../types'; +import { Oas3TranslateFunction } from '../types'; import { translateMediaTypeObject } from './content'; +import { translateToExample } from './examples'; -export const translateRequestBody: Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> = - function (requestBodyObject) { - const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); - - if (isRequestBodyObject(resolvedRequestBodyObject)) { - return { - contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), - - ...pickBy( - { - required: resolvedRequestBodyObject.required, - description: resolvedRequestBodyObject.description, - }, - isNonNullable, - ), - }; - } - - return { contents: [] }; - }; +export const translateRequestBody = withContext< + Oas3TranslateFunction<[requestBodyObject: unknown], IHttpOperationRequestBody> +>(function (requestBodyObject) { + const resolvedRequestBodyObject = this.maybeResolveLocalRef(requestBodyObject); + const id = this.generateId(`http_request_body-${this.parentId}`); + + if (isRequestBodyObject(resolvedRequestBodyObject)) { + return { + id, + contents: entries(resolvedRequestBodyObject.content).map(translateMediaTypeObject, this).filter(isNonNullable), + + ...pickBy( + { + required: resolvedRequestBodyObject.required, + }, + isBoolean, + ), -const translateParameterObjectSchema: Oas3TranslateFunction< - [parameterObject: Fragment], - Optional -> = function (parameterObject) { + ...pickBy( + { + description: resolvedRequestBodyObject.description, + }, + isString, + ), + }; + } + + return { id, contents: [] }; +}); + +const translateParameterObjectSchema = withContext< + Oas3TranslateFunction<[parameterObject: Record], Optional> +>(function (parameterObject) { if (!isPlainObject(parameterObject.schema)) return; return translateSchemaObject.call(this, { ...parameterObject.schema, ...('example' in parameterObject ? { example: parameterObject.example } : null), }); -}; +}); -const translateToExample: Oas3TranslateFunction< - ArrayCallbackParameters<[key: string, example: unknown]>, - Optional -> = function ([key, example]) { - if (!isPlainObject(example)) return; +export const translateParameterObject = withContext< + Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> +>(function (parameterObject) { + const kind = parameterObject.in === 'path' ? 'path_param' : parameterObject.in; + const name = parameterObject.name; + const id = this.generateId(`http_${kind}-${this.parentId}-${name}`); + const schema = translateParameterObjectSchema.call(this, parameterObject); - if (!('value' in example) && typeof example.externalValue !== 'string') return; + const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); + const hasDefaultExample = examples.some(({ key }) => key.includes('default')); return { - key, - - ...(typeof example.externalValue === 'string' - ? { externalValue: example.externalValue } - : { value: example.value }), + id, + name, + style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, + examples: [ + !hasDefaultExample && parameterObject.example !== undefined + ? translateToDefaultExample.call(this, 'default', parameterObject.example) + : undefined, + ...examples, + ].filter(isNonNullable), ...pickBy( { - summary: example.summary, - description: example.description, + description: parameterObject.description, }, isString, ), - }; -}; - -export const translateParameterObject: Oas3TranslateFunction<[parameterObject: ParameterObject], IHttpParam> = - function (parameterObject) { - const examples = entries(parameterObject.examples).map(translateToExample, this).filter(isNonNullable); - - const hasDefaultExample = examples.some(({ key }) => key.includes('default')); - const schema = translateParameterObjectSchema.call(this, parameterObject); - return { - name: parameterObject.name, - style: isValidParamStyle(parameterObject.style) ? parameterObject.style : HttpParamStyles.Simple, - examples: - 'example' in parameterObject && !hasDefaultExample - ? [{ key: 'default', value: parameterObject.example }, ...examples] - : examples, - - ...pickBy( - { - description: parameterObject.description, - }, - isString, - ), - - ...pickBy( - { - explode: parameterObject.explode, - deprecated: parameterObject.deprecated, - }, - isBoolean, - ), + ...pickBy( + { + deprecated: parameterObject.deprecated, + required: parameterObject.required, + explode: parameterObject.explode, + }, + isBoolean, + ), - ...pickBy( - { - schema, - content: parameterObject.content, - }, - isPlainObject, - ), - }; + ...pickBy( + { + schema, + content: parameterObject.content, + }, + isPlainObject, + ), + }; +}); + +const iterateOasParams = createOasParamsIterator(OasVersion.OAS3); + +export const translateToRequest = withContext< + Oas3TranslateFunction<[path: Record, operation: Record], IHttpOperationRequest> +>(function (path, operation) { + const params: Omit & { header: IHttpHeaderParam[] } = { + header: [], + query: [], + cookie: [], + path: [], }; -export const translateToRequest: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IHttpOperationRequest> = - function (path, operation) { - const params: Omit & { header: IHttpHeaderParam[] } = { - header: [], - query: [], - cookie: [], - path: [], - }; - - for (const param of getValidOasParameters(this.document, OasVersion.OAS3, operation.parameters, path.parameters)) { - const { in: key } = param; - const target = params[key]; - if (!Array.isArray(target)) continue; + for (const param of iterateOasParams.call(this, path, operation)) { + const { in: key } = param; + const target = params[key]; + if (!Array.isArray(target)) continue; - target.push(translateParameterObject.call(this, param) as any); - } + target.push(translateParameterObject.call(this, param) as any); + } - return { - body: translateRequestBody.call(this, operation?.requestBody), - headers: params.header, - query: params.query, - cookie: params.cookie, - path: params.path, - }; + return { + body: translateRequestBody.call(this, operation?.requestBody), + headers: params.header, + query: params.query, + cookie: params.cookie, + path: params.path, }; +}); diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index 2f7225f9..a569b283 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,21 +1,28 @@ import type { IHttpOperationResponse, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import type { ArrayCallbackParameters } from '../../types'; +import { getSharedKey } from '../../oas/resolver'; +import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; import { isResponseObject } from '../guards'; -import type { Oas3TranslateFunction } from '../types'; +import { Oas3TranslateFunction } from '../types'; import { translateHeaderObject, translateMediaTypeObject } from './content'; -const translateToResponse: Oas3TranslateFunction< - ArrayCallbackParameters<[statusCode: string, response: unknown]>, - Optional -> = function ([statusCode, response]) { +const translateToResponse = withContext< + Oas3TranslateFunction< + ArrayCallbackParameters<[statusCode: string, response: unknown]>, + Optional + > +>(function ([statusCode, response]) { const resolvedResponse = this.maybeResolveLocalRef(response); if (!isResponseObject(resolvedResponse)) return; + const actualKey = this.context === 'service' ? getSharedKey(resolvedResponse) : statusCode; + return { + id: this.generateId(`http_response-${this.parentId}-${actualKey}`), code: statusCode, headers: entries(resolvedResponse.headers).map(translateHeaderObject, this).filter(isNonNullable), contents: entries(resolvedResponse.content).map(translateMediaTypeObject, this).filter(isNonNullable), @@ -27,7 +34,7 @@ const translateToResponse: Oas3TranslateFunction< isString, ), }; -}; +}); export const translateToResponses: Oas3TranslateFunction<[responses: unknown], IHttpOperationResponse[]> = function ( responses, diff --git a/src/oas3/transformers/securities.ts b/src/oas3/transformers/securities.ts index 53618e93..cfd11162 100644 --- a/src/oas3/transformers/securities.ts +++ b/src/oas3/transformers/securities.ts @@ -1,27 +1,34 @@ import { isPlainObject } from '@stoplight/json'; import type { IApiKeySecurityScheme, IOauthFlowObjects, Optional } from '@stoplight/types'; +import { HttpSecurityScheme } from '@stoplight/types'; import type { SecuritySchemeObject } from 'openapi3-ts'; +import { withContext } from '../../context'; import { isNonNullable } from '../../guards'; import { ArrayCallbackParameters } from '../../types'; -import { getSecurities, SecurityWithKey } from '../accessors'; +import { getSecurities } from '../accessors'; import { isOAuthFlowObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], SecurityWithKey[][]> = +export const translateToSecurities: Oas3TranslateFunction<[operationSecurities: unknown], HttpSecurityScheme[][]> = function (operationSecurities) { const securities = getSecurities(this.document, operationSecurities); return securities.map(security => security.map(translateToSingleSecurity, this).filter(isNonNullable)); }; -export const translateToSingleSecurity: Oas3TranslateFunction< - [ArrayCallbackParameters & { type: 'mutualTLS' })>[0]], - Optional -> = function (securityScheme) { - const { key } = securityScheme; - - const baseObject: { key: string; description?: string } = { +export const translateToSingleSecurity = withContext< + Oas3TranslateFunction< + [ + ArrayCallbackParameters< + [key: string, security: SecuritySchemeObject | (Omit & { type: 'mutualTLS' })] + >[0], + ], + Optional + > +>(function ([key, securityScheme]) { + const baseObject: { id: string; key: string; description?: string } = { + id: this.generateId(`http_security-${this.ids.service}-${key}`), key, }; @@ -79,7 +86,7 @@ export const translateToSingleSecurity: Oas3TranslateFunction< } return undefined; -}; +}); function transformFlows(flows: Optional): IOauthFlowObjects { const transformedFlows: IOauthFlowObjects = {}; diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index 9a22e861..6e9f5265 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,38 +1,41 @@ import type { INodeVariable, IServer, Optional } from '@stoplight/types'; import pickBy = require('lodash.pickby'); +import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; -import { ArrayCallbackParameters, Fragment } from '../../types'; +import { ArrayCallbackParameters } from '../../types'; import { entries } from '../../utils'; import { isServerObject, isServerVariableObject } from '../guards'; import { Oas3TranslateFunction } from '../types'; -export const translateToServers: Oas3TranslateFunction<[path: Fragment, operation: Fragment], IServer[]> = function ( - path, - operation, -) { +export const translateToServers = withContext< + Oas3TranslateFunction<[path: Record, operation: Record], IServer[]> +>(function (path, operation) { let servers; if (Array.isArray(operation.servers)) { servers = operation.servers; } else if (Array.isArray(path.servers)) { servers = path.servers; + this.context = 'path'; } else if (Array.isArray(this.document.servers)) { servers = this.document.servers; + this.context = 'service'; } else { return []; } return servers.map(translateToServer, this).filter(isNonNullable); -}; +}); -export const translateToServer: Oas3TranslateFunction, Optional> = function ( - server, -) { +export const translateToServer = withContext< + Oas3TranslateFunction, Optional> +>(function (server) { if (!isServerObject(server)) return; const variables = translateServerVariables.call(this, server.variables); return { + id: this.generateId(`http_server-${this.parentId}-${server.url}`), url: server.url, ...pickBy( @@ -50,7 +53,7 @@ export const translateToServer: Oas3TranslateFunction { }), ).toEqual( expect.objectContaining({ - security: [[{ key: 'http-0', scheme: 'basic', type: 'http' }]], + security: [ + [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'http-0', scheme: 'basic', type: 'http' }], + ], }), ); }); @@ -123,6 +125,7 @@ describe('transformPostmanCollectionOperation()', () => { request: expect.objectContaining({ headers: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), description: 'Hawk Authorization Header', name: 'Authorization', required: true, @@ -166,7 +169,15 @@ describe('transformPostmanCollectionOperation()', () => { ).toEqual( expect.objectContaining({ request: expect.objectContaining({ - query: [{ description: 'OAuth2 Access Token', name: 'access_token', required: true, style: 'form' }], + query: [ + { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + description: 'OAuth2 Access Token', + name: 'access_token', + required: true, + style: 'form', + }, + ], }), }), ); @@ -223,7 +234,7 @@ describe('transformPostmanCollectionOperation()', () => { }), ).toEqual( expect.objectContaining({ - servers: [{ url: 'https://example.com:666' }], + servers: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), url: 'https://example.com:666' }], }), ); }); @@ -260,9 +271,20 @@ describe('transformPostmanCollectionOperation()', () => { ).toEqual( expect.objectContaining({ request: expect.objectContaining({ - body: { contents: [{ examples: [{ key: 'default', value: 'testTESTtest' }], mediaType: 'text/plain' }] }, + body: { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + contents: [ + { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'testTESTtest' }, + ], + mediaType: 'text/plain', + }, + ], + }, }), - servers: [{ url: 'https://example.com' }], + servers: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), url: 'https://example.com' }], }), ); }); @@ -331,8 +353,10 @@ describe('transformPostmanCollectionOperations()', () => { request: expect.objectContaining({ headers: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'a header', }, @@ -345,8 +369,10 @@ describe('transformPostmanCollectionOperations()', () => { required: false, }, { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'b header', }, diff --git a/src/postman/__tests__/service.test.ts b/src/postman/__tests__/service.test.ts index 2f67d4f2..6d111dbf 100644 --- a/src/postman/__tests__/service.test.ts +++ b/src/postman/__tests__/service.test.ts @@ -30,6 +30,7 @@ describe('transformPostmanCollectionService()', () => { id: expect.any(String), securitySchemes: [ { + id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', diff --git a/src/postman/__tests__/util.test.ts b/src/postman/__tests__/util.test.ts index 43408104..2b5ae3c6 100644 --- a/src/postman/__tests__/util.test.ts +++ b/src/postman/__tests__/util.test.ts @@ -6,7 +6,7 @@ describe('transformStringValueToSchema()', () => { it('returns param with schema and example', () => { expect(transformStringValueToSchema('test')).toEqual({ schema: { type: 'string' }, - examples: [{ key: 'default', value: 'test' }], + examples: [{ id: expect.any(String), key: 'default', value: 'test' }], }); }); }); diff --git a/src/postman/id.ts b/src/postman/id.ts new file mode 100644 index 00000000..e39b995f --- /dev/null +++ b/src/postman/id.ts @@ -0,0 +1,8 @@ +export function generateId() { + return ( + '_gen_' + + Math.round(Math.pow(8, 6) * Math.random()) + .toString(16) + .padStart(6, '0') + ); +} diff --git a/src/postman/operation.ts b/src/postman/operation.ts index 57ac6653..e70106d6 100644 --- a/src/postman/operation.ts +++ b/src/postman/operation.ts @@ -1,5 +1,5 @@ -import { HttpSecurityScheme, IHttpOperation } from '@stoplight/types'; -import { Collection, CollectionDefinition, Item, RequestAuth, Url } from 'postman-collection'; +import type { HttpSecurityScheme, IHttpOperation } from '@stoplight/types'; +import type { Collection, CollectionDefinition, Item, RequestAuth, Url } from 'postman-collection'; import { mergeOperations, mergeResponses } from '../merge'; import { transformRequest } from './transformers/request'; @@ -11,7 +11,7 @@ import { transformSecuritySchemes, } from './transformers/securityScheme'; import { transformServer } from './transformers/server'; -import { PostmanCollectionHttpOperationTransformer } from './types'; +import type { PostmanCollectionHttpOperationTransformer } from './types'; import { resolveCollection, transformDescriptionDefinition } from './util'; export const transformPostmanCollectionOperations = (document: CollectionDefinition): IHttpOperation[] => { diff --git a/src/postman/service.ts b/src/postman/service.ts index b2e3888a..4a781b3d 100644 --- a/src/postman/service.ts +++ b/src/postman/service.ts @@ -1,6 +1,6 @@ -import { CollectionDefinition } from 'postman-collection'; +import type { CollectionDefinition } from 'postman-collection'; -import { HttpServiceTransformer } from '../types'; +import type { HttpServiceTransformer } from '../types'; import { isStandardSecurityScheme, transformSecuritySchemes } from './transformers/securityScheme'; import { resolveCollection, resolveVersion, transformDescriptionDefinition } from './util'; diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index fd51a78c..006de934 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -7,8 +7,10 @@ describe('transformQueryParam()', () => { describe('value is set', () => { it('transforms correctly', () => { expect(transformQueryParam(new QueryParam({ key: 'testKey', value: 'testValue' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'testValue', }, @@ -26,6 +28,7 @@ describe('transformQueryParam()', () => { describe('value is null', () => { it('transforms correctly', () => { expect(transformQueryParam(new QueryParam({ key: 'testKey', value: null }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testKey', style: 'form', required: true, @@ -36,6 +39,7 @@ describe('transformQueryParam()', () => { describe('key is null', () => { it('transforms correctly with key being empty string', () => { expect(transformQueryParam(new QueryParam({ key: null, value: null }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: '', style: 'form', required: true, @@ -48,6 +52,7 @@ describe('transformHeader()', () => { describe('value is defined', () => { it('result contains schema', () => { expect(transformHeader(new Header({ key: 'testKey', value: 'some string' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testkey', schema: { type: 'string', @@ -56,6 +61,7 @@ describe('transformHeader()', () => { required: true, examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: 'some string', }, @@ -67,6 +73,7 @@ describe('transformHeader()', () => { describe('value is not defined', () => { it('results does not contain schema', () => { expect(transformHeader(new Header({ key: 'testKey' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'testkey', style: 'simple', required: true, @@ -78,8 +85,8 @@ describe('transformHeader()', () => { describe('transformPathParams()', () => { it('transforms correctly', () => { expect(transformPathParams(['elem1', ':param1', ':param2', 'elem2'])).toEqual([ - { name: 'param1', style: 'simple', required: true }, - { name: 'param2', style: 'simple', required: true }, + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'param1', style: 'simple', required: true }, + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), name: 'param2', style: 'simple', required: true }, ]); }); }); @@ -91,9 +98,11 @@ describe('transformBody()', () => { describe('body is correctly defined json', () => { it('returns body containing example, schema and media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '{"a":"b"}' }), 'application/nice+json')).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { a: 'b' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { a: 'b' } }], mediaType: 'application/nice+json', schema: { $schema: 'http://json-schema.org/draft-07/schema#', @@ -116,9 +125,11 @@ describe('transformBody()', () => { describe('body is not a correct JSON', () => { it('returns body containing example and media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '"a":"b"' }), 'application/json')).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: '"a":"b"' }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: '"a":"b"' }], mediaType: 'application/json', }, ], @@ -131,9 +142,11 @@ describe('transformBody()', () => { describe('media type is defined', () => { it('returns body containing example and given media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: '' }), 'application/xml')).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: '' }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: '' }], mediaType: 'application/xml', }, ], @@ -144,9 +157,13 @@ describe('transformBody()', () => { describe('media type is not defined', () => { it('returns body containing example and text/plain media type', () => { expect(transformBody(new RequestBody({ mode: 'raw', raw: "I'm a goat. Bleeet!" }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: "I'm a goat. Bleeet!" }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: "I'm a goat. Bleeet!" }, + ], mediaType: 'text/plain', }, ], @@ -179,9 +196,13 @@ describe('transformBody()', () => { 'multipart/test+form-data', ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1', k2: 'v2' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1', k2: 'v2' } }, + ], mediaType: 'multipart/test+form-data', schema: { type: 'object', @@ -206,9 +227,11 @@ describe('transformBody()', () => { }), ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1' } }], mediaType: 'multipart/form-data', schema: { type: 'object', @@ -234,9 +257,13 @@ describe('transformBody()', () => { ); expect(result).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: expect.any(Object) }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: expect.any(Object) }, + ], mediaType: 'multipart/test+form-data', schema: { type: 'object', @@ -272,9 +299,11 @@ describe('transformBody()', () => { describe('body is not defined', () => { it('returns default empty body', () => { expect(transformBody(new RequestBody({ mode: 'formdata' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: {} }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: {} }], mediaType: 'multipart/form-data', schema: { properties: {}, type: 'object' }, }, @@ -300,9 +329,13 @@ describe('transformBody()', () => { 'application/test+x-www-form-urlencoded', ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1', k2: 'v2' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [ + { id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1', k2: 'v2' } }, + ], mediaType: 'application/test+x-www-form-urlencoded', schema: { type: 'object', @@ -327,9 +360,11 @@ describe('transformBody()', () => { }), ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: { k1: 'v1' } }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { k1: 'v1' } }], mediaType: 'application/x-www-form-urlencoded', schema: { type: 'object', @@ -347,9 +382,11 @@ describe('transformBody()', () => { describe('body is not defined', () => { it('returns default empty body', () => { expect(transformBody(new RequestBody({ mode: 'urlencoded' }))).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), contents: [ { - examples: [{ key: 'default', value: {} }], + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), + examples: [{ id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: {} }], mediaType: 'application/x-www-form-urlencoded', schema: { properties: {}, type: 'object' }, }, diff --git a/src/postman/transformers/__tests__/request.test.ts b/src/postman/transformers/__tests__/request.test.ts index 679d0da3..27b782b9 100644 --- a/src/postman/transformers/__tests__/request.test.ts +++ b/src/postman/transformers/__tests__/request.test.ts @@ -15,26 +15,30 @@ describe('transformRequest()', () => { ), ).toEqual({ body: { + id: expect.any(String), contents: [ { - examples: [{ key: 'default', value: 'test' }], + id: expect.any(String), + examples: [{ id: expect.any(String), key: 'default', value: 'test' }], mediaType: 'text/plain', }, ], }, headers: [ { + id: expect.any(String), name: 'header', schema: { type: 'string' }, - examples: [{ key: 'default', value: 'a header' }], + examples: [{ id: expect.any(String), key: 'default', value: 'a header' }], style: 'simple', required: true, }, ], - path: [{ name: 'param', style: 'simple', required: true }], + path: [{ id: expect.any(String), name: 'param', style: 'simple', required: true }], query: [ { - examples: [{ key: 'default', value: 'b' }], + id: expect.any(String), + examples: [{ id: expect.any(String), key: 'default', value: 'b' }], schema: { type: 'string' }, name: 'a', style: 'form', diff --git a/src/postman/transformers/__tests__/response.test.ts b/src/postman/transformers/__tests__/response.test.ts index 93cb7ea4..dc502b9a 100644 --- a/src/postman/transformers/__tests__/response.test.ts +++ b/src/postman/transformers/__tests__/response.test.ts @@ -23,11 +23,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), code: '200', contents: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), examples: [ { + id: expect.stringMatching(/^_gen_[0-9a-f]{6}$/), key: 'default', value: { "I'm a JSON": 'Jieeeet!', @@ -51,8 +54,10 @@ describe('transformResponse()', () => { ], headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'application/json', }, @@ -65,8 +70,10 @@ describe('transformResponse()', () => { style: 'simple', }, { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Domain=.example.com; Path=/', }, @@ -90,6 +97,7 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [], }); @@ -108,6 +116,7 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', description: 'Test', headers: [], @@ -138,11 +147,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=300; Domain=example.com; Path=/; Secure; HttpOnly', @@ -174,11 +186,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Domain=example.com; Path=/', }, @@ -207,11 +222,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: '=; Domain=example.com; Path=/', }, @@ -246,11 +264,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Domain=example.com; Path=/; a=p; u=d', }, @@ -282,11 +303,14 @@ describe('transformResponse()', () => { }), ), ).toEqual({ + id: expect.any(String), code: '200', headers: [ { + id: expect.any(String), examples: [ { + id: expect.any(String), key: 'default', value: 'eat=functions; Expires=Fri, 11 Aug 2017 09:04:08 GMT; Domain=example.com; Path=/', }, diff --git a/src/postman/transformers/__tests__/securitySchemes.spec.ts b/src/postman/transformers/__tests__/securitySchemes.spec.ts index 13e0828a..396c4fa4 100644 --- a/src/postman/transformers/__tests__/securitySchemes.spec.ts +++ b/src/postman/transformers/__tests__/securitySchemes.spec.ts @@ -33,6 +33,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'basic', @@ -70,6 +71,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'digest', @@ -97,6 +99,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-http', type: 'http', scheme: 'bearer', @@ -135,6 +138,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-apiKey', type: 'apiKey', name: 'TestApiKey', @@ -168,6 +172,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-apiKey', type: 'apiKey', name: 'TestApiKey', @@ -208,12 +213,14 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { + id: expect.any(String), key: 'default', value: 'OAuth realm="karol@stoplight.io",oauth_consumer_key="a_consumer_key",oauth_token="a_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="123123123123",oauth_nonce="a",oauth_version="1.0",oauth_signature="a_signature"', @@ -241,12 +248,14 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { + id: expect.any(String), key: 'default', value: 'OAuth realm="a_realm",oauth_consumer_key="a_consumer_key",oauth_token="a_token",oauth_signature_method="HMAC-SHA1",oauth_timestamp="0",oauth_nonce="a",oauth_version="1.0",oauth_signature="a_signature"', @@ -274,29 +283,32 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, - { name: 'oauth_token', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: false, - examples: [{ key: 'default', value: 'HMAC-SHA1' }], + examples: [{ id: expect.any(String), key: 'default', value: 'HMAC-SHA1' }], }, { + id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: false, schema: { type: 'string' }, - examples: [{ key: 'default', value: '123123123123' }], + examples: [{ id: expect.any(String), key: 'default', value: '123123123123' }], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: false, - examples: [{ key: 'default', value: '1.0' }], + examples: [{ id: expect.any(String), key: 'default', value: '1.0' }], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, ], }); }); @@ -317,29 +329,32 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, - { name: 'oauth_token', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: false, examples: [], }, { + id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: false, schema: { type: 'string' }, examples: [], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: false }, { + id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: false, examples: [], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, + { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: false }, ], }); }); @@ -359,29 +374,32 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: true }, - { name: 'oauth_token', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_token', style: HttpParamStyles.Form, required: true }, { + id: expect.any(String), name: 'oauth_signature_method', style: HttpParamStyles.Form, required: true, - examples: [{ key: 'default', value: 'HMAC-SHA1' }], + examples: [{ id: expect.any(String), key: 'default', value: 'HMAC-SHA1' }], }, { + id: expect.any(String), name: 'oauth_timestamp', style: HttpParamStyles.Form, required: true, schema: { type: 'string' }, - examples: [{ key: 'default', value: '123123123123' }], + examples: [{ id: expect.any(String), key: 'default', value: '123123123123' }], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_nonce', style: HttpParamStyles.Form, required: true }, { + id: expect.any(String), name: 'oauth_version', style: HttpParamStyles.Form, required: true, - examples: [{ key: 'default', value: '1.0' }], + examples: [{ id: expect.any(String), key: 'default', value: '1.0' }], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required: true }, + { id: expect.any(String), name: 'oauth_signature', style: HttpParamStyles.Form, required: true }, ], }); }); @@ -414,6 +432,7 @@ describe('transformSecurityScheme()', () => { ).toEqual({ type: 'securityScheme', securityScheme: { + id: expect.any(String), key: 'auth-oauth2', description: 'OAuth2 Access Token', scheme: 'bearer', @@ -448,6 +467,7 @@ describe('transformSecurityScheme()', () => { type: 'queryParams', queryParams: [ { + id: expect.any(String), name: 'access_token', style: HttpParamStyles.Form, description: 'OAuth2 Access Token', @@ -483,6 +503,7 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'Hawk Authorization Header', @@ -517,16 +538,19 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'X-Amz-Security-Token', style: HttpParamStyles.Simple, required: true, }, { + id: expect.any(String), name: 'X-Amz-Date', style: HttpParamStyles.Simple, required: true, }, { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -559,6 +583,7 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'Akamai EdgeGrid Authorization Header', @@ -588,6 +613,7 @@ describe('transformSecurityScheme()', () => { type: 'headerParams', headerParams: [ { + id: expect.any(String), name: 'Authorization', style: HttpParamStyles.Simple, description: 'NTLM Authorization Header', @@ -647,6 +673,7 @@ describe('transformPostmanSecuritySchemes()', () => { ).toEqual([ { securityScheme: { + id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', @@ -655,6 +682,7 @@ describe('transformPostmanSecuritySchemes()', () => { }, { securityScheme: { + id: expect.any(String), key: 'http-1', scheme: 'digest', type: 'http', @@ -753,6 +781,7 @@ describe('transformPostmanSecuritySchemes()', () => { ).toEqual([ { securityScheme: { + id: expect.any(String), key: 'http-0', scheme: 'basic', type: 'http', @@ -767,26 +796,26 @@ describe('transformPostmanSecuritySchemes()', () => { describe.each<[string, PostmanSecurityScheme, PostmanSecurityScheme, boolean]>([ [ 'two equal security schemes', - { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, - { type: 'securityScheme', securityScheme: { key: '2', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '2', type: 'http', scheme: 'basic' } }, true, ], [ 'different types', - { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, { type: 'headerParams', headerParams: [] }, false, ], [ 'two different', - { type: 'securityScheme', securityScheme: { key: '1', type: 'http', scheme: 'basic' } }, - { type: 'securityScheme', securityScheme: { key: '2', type: 'http', scheme: 'digest' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '1', type: 'http', scheme: 'basic' } }, + { type: 'securityScheme', securityScheme: { id: '', key: '2', type: 'http', scheme: 'digest' } }, false, ], [ 'two equal query params', - { type: 'queryParams', queryParams: [{ name: 'token', style: HttpParamStyles.Form }] }, - { type: 'queryParams', queryParams: [{ name: 'token', style: HttpParamStyles.Form }] }, + { type: 'queryParams', queryParams: [{ id: '', name: 'token', style: HttpParamStyles.Form }] }, + { type: 'queryParams', queryParams: [{ id: '', name: 'token', style: HttpParamStyles.Form }] }, true, ], ])('given %s security schemes', (desc, scheme1, scheme2, result) => { diff --git a/src/postman/transformers/__tests__/server.test.ts b/src/postman/transformers/__tests__/server.test.ts index 4410a412..4f623f42 100644 --- a/src/postman/transformers/__tests__/server.test.ts +++ b/src/postman/transformers/__tests__/server.test.ts @@ -6,13 +6,19 @@ describe('transformServer()', () => { describe('host is defined', () => { describe('port is defined', () => { it('produces server with port', () => { - expect(transformServer(new Url('https://example.com:666/path'))).toEqual({ url: 'https://example.com:666' }); + expect(transformServer(new Url('https://example.com:666/path'))).toEqual({ + id: expect.any(String), + url: 'https://example.com:666', + }); }); }); describe('port is not defined', () => { it('produces server without port', () => { - expect(transformServer(new Url('https://example.com/path'))).toEqual({ url: 'https://example.com' }); + expect(transformServer(new Url('https://example.com/path'))).toEqual({ + id: expect.any(String), + url: 'https://example.com', + }); }); }); }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 341e8fa4..96370a5c 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -9,14 +9,16 @@ import { import type { JSONSchema7 } from 'json-schema'; // @ts-ignore import * as jsonSchemaGenerator from 'json-schema-generator'; -import { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; +import type { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; import { convertSchema } from '../../oas/transformers/schema'; +import { generateId } from '../id'; import { transformDescriptionDefinition, transformStringValueToSchema } from '../util'; export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { return { + id: generateId(), name: queryParam.key || '', style: HttpParamStyles.Form, required: true, @@ -26,6 +28,7 @@ export function transformQueryParam(queryParam: QueryParam): IHttpQueryParam { export function transformHeader(header: Header): IHttpHeaderParam { return { + id: generateId(), name: header.key.toLowerCase(), style: HttpParamStyles.Simple, required: true, @@ -37,6 +40,7 @@ export function transformPathParams(segments: string[]): IHttpPathParam[] { return segments.reduce((params, segment) => { if (segment.startsWith(':')) { params.push({ + id: generateId(), name: segment.substring(1), style: HttpParamStyles.Simple, required: true, @@ -51,17 +55,19 @@ export function transformBody(body: RequestBody, mediaType?: string): IHttpOpera switch (body.mode) { case 'raw': if (!body.raw) return; - return { contents: [transformRawBody(body.raw, mediaType)] }; + return { id: generateId(), contents: [transformRawBody(body.raw, mediaType)] }; case 'formdata': if (!body.formdata) return; return { + id: generateId(), contents: [transformParamsBody(body.formdata, mediaType || 'multipart/form-data')], }; case 'urlencoded': if (!body.urlencoded) return; return { + id: generateId(), contents: [transformParamsBody(body.urlencoded, mediaType || 'application/x-www-form-urlencoded')], }; } @@ -75,9 +81,11 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): const parsed = JSON.parse(raw); return { + id: generateId(), mediaType, examples: [ { + id: generateId(), key: 'default', value: parsed, }, @@ -90,9 +98,11 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): } return { + id: generateId(), mediaType, examples: [ { + id: generateId(), key: 'default', value: raw, }, @@ -116,6 +126,7 @@ function transformParamsBody( }); return { + id: generateId(), mediaType, schema: { type: 'object', @@ -126,6 +137,7 @@ function transformParamsBody( }, examples: [ { + id: generateId(), key: 'default', value: paramsList.reduce((values, param) => { values[param.name] = param.value; @@ -135,12 +147,3 @@ function transformParamsBody( ], }; } - -function generateId() { - return ( - '_gen_' + - Math.round(Math.pow(8, 6) * Math.random()) - .toString(16) - .padStart(6, '0') - ); -} diff --git a/src/postman/transformers/response.ts b/src/postman/transformers/response.ts index 97f8b350..a2efe60c 100644 --- a/src/postman/transformers/response.ts +++ b/src/postman/transformers/response.ts @@ -1,6 +1,7 @@ import { HttpParamStyles, IHttpHeaderParam, IHttpOperationResponse } from '@stoplight/types'; import { Cookie, Response } from 'postman-collection'; +import { generateId } from '../id'; import { transformDescriptionDefinition } from '../util'; import { transformHeader, transformRawBody } from './params'; @@ -9,6 +10,7 @@ export function transformResponse(response: Response): IHttpOperationResponse { const mediaType = response.headers.get('content-type'); return { + id: generateId(), code: String(response.code), description: response.description && transformDescriptionDefinition(response.description), headers: headers.concat(response.cookies.map(transformCookie).filter((c: Cookie) => c)), @@ -29,9 +31,11 @@ function transformCookie(cookie: Cookie): IHttpHeaderParam | undefined { if (cookie.extensions) params.push(...cookie.extensions.map(({ key, value }) => `${key}=${value}`)); return { + id: generateId(), name: 'set-cookie', examples: [ { + id: generateId(), key: 'default', value: params.join('; '), }, diff --git a/src/postman/transformers/securityScheme.ts b/src/postman/transformers/securityScheme.ts index 816b2cb3..ba48f408 100644 --- a/src/postman/transformers/securityScheme.ts +++ b/src/postman/transformers/securityScheme.ts @@ -1,6 +1,8 @@ import { HttpParamStyles, HttpSecurityScheme, IHttpHeaderParam, IHttpQueryParam } from '@stoplight/types'; -import { Collection, RequestAuth } from 'postman-collection'; -import isEqual = require('lodash.isequal'); +import type { Collection, RequestAuth } from 'postman-collection'; + +import { isEqual } from '../../utils'; +import { generateId } from '../id'; export type PostmanSecurityScheme = StandardSecurityScheme | QuerySecurityScheme | HeaderSecurityScheme; @@ -36,12 +38,14 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, description: 'OAuth1 Authorization Header', required: true, examples: [ { + id: generateId(), key: 'default', value: 'OAuth ' + @@ -69,31 +73,38 @@ export function transformSecurityScheme( return { type: 'queryParams', queryParams: [ - { name: 'oauth_consumer_key', style: HttpParamStyles.Form, required }, - { name: 'oauth_token', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_consumer_key', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_token', style: HttpParamStyles.Form, required }, { + id: generateId(), name: 'oauth_signature_method', style: HttpParamStyles.Form, required, examples: parameters.has('signatureMethod') - ? [{ key: 'default', value: parameters.get('signatureMethod') }] + ? [{ id: generateId(), key: 'default', value: parameters.get('signatureMethod') }] : [], }, { + id: generateId(), name: 'oauth_timestamp', style: HttpParamStyles.Form, required, schema: { type: 'string' }, - examples: parameters.has('timestamp') ? [{ key: 'default', value: parameters.get('timestamp') }] : [], + examples: parameters.has('timestamp') + ? [{ id: generateId(), key: 'default', value: parameters.get('timestamp') }] + : [], }, - { name: 'oauth_nonce', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_nonce', style: HttpParamStyles.Form, required }, { + id: generateId(), name: 'oauth_version', style: HttpParamStyles.Form, required, - examples: parameters.has('version') ? [{ key: 'default', value: parameters.get('version') }] : [], + examples: parameters.has('version') + ? [{ id: generateId(), key: 'default', value: parameters.get('version') }] + : [], }, - { name: 'oauth_signature', style: HttpParamStyles.Form, required }, + { id: generateId(), name: 'oauth_signature', style: HttpParamStyles.Form, required }, ], }; } @@ -103,6 +114,7 @@ export function transformSecurityScheme( type: 'queryParams', queryParams: [ { + id: generateId(), name: 'access_token', description: 'OAuth2 Access Token', style: HttpParamStyles.Form, @@ -114,6 +126,7 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { + id: generateId(), key: nextKey('oauth2'), type: 'http', scheme: 'bearer', @@ -126,6 +139,7 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { + id: generateId(), key: nextKey('apiKey'), type: 'apiKey', name: parameters.get('key'), @@ -139,6 +153,7 @@ export function transformSecurityScheme( return { type: 'securityScheme', securityScheme: { + id: generateId(), key: nextKey('http'), type: 'http', scheme: auth.type, @@ -150,6 +165,7 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', description: 'Hawk Authorization Header', required: true, @@ -167,16 +183,19 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'X-Amz-Security-Token', style: HttpParamStyles.Simple, required: true, }, { + id: generateId(), name: 'X-Amz-Date', style: HttpParamStyles.Simple, required: true, }, { + id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -190,6 +209,7 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', style: HttpParamStyles.Simple, required: true, @@ -203,6 +223,7 @@ export function transformSecurityScheme( type: 'headerParams', headerParams: [ { + id: generateId(), name: 'Authorization', description: 'NTLM Authorization Header', required: true, @@ -227,9 +248,7 @@ export function isPostmanSecuritySchemeEqual(pss1: PostmanSecurityScheme, pss2: if (pss1.type !== pss2.type) return false; if (isStandardSecurityScheme(pss1) && isStandardSecurityScheme(pss2)) { - const { key: _key, ...pss1SecurityScheme } = pss1.securityScheme; - const { key: _key2, ...pss2SecurityScheme } = pss2.securityScheme; - return isEqual(pss1SecurityScheme, pss2SecurityScheme); + return isEqual({ ...pss1.securityScheme, key: '' }, { ...pss2.securityScheme, key: '' }); } return isEqual(pss1, pss2); diff --git a/src/postman/transformers/server.ts b/src/postman/transformers/server.ts index e2898163..ccda6fd3 100644 --- a/src/postman/transformers/server.ts +++ b/src/postman/transformers/server.ts @@ -1,10 +1,12 @@ -import { IServer } from '@stoplight/types'; -import { Url } from 'postman-collection'; +import type { IServer } from '@stoplight/types'; +import type { Url } from 'postman-collection'; + +import { generateId } from '../id'; export function transformServer(url: Url): IServer | undefined { try { const origin = new URL(url.toString()).origin; - return origin ? { url: origin } : undefined; + return origin ? { id: generateId(), url: origin } : undefined; } catch { return undefined; } diff --git a/src/postman/types.ts b/src/postman/types.ts index 0503aeff..350c7555 100644 --- a/src/postman/types.ts +++ b/src/postman/types.ts @@ -1,6 +1,6 @@ -import { CollectionDefinition } from 'postman-collection'; +import type { CollectionDefinition } from 'postman-collection'; -import { HttpOperationTransformer } from '../types'; +import type { HttpOperationTransformer } from '../types'; export type PostmanCollectionHttpOperationTransformer = HttpOperationTransformer<{ document: CollectionDefinition; diff --git a/src/postman/util.ts b/src/postman/util.ts index 8c8e321a..ca5277f6 100644 --- a/src/postman/util.ts +++ b/src/postman/util.ts @@ -1,10 +1,13 @@ -import { IHttpParam } from '@stoplight/types'; +import type { IHttpParam } from '@stoplight/types'; import { Collection, CollectionDefinition, DescriptionDefinition, Version } from 'postman-collection'; +import { generateId } from './id'; + export function transformStringValueToSchema(value: string): Pick { return { examples: [ { + id: generateId(), key: 'default', value, }, diff --git a/src/types.ts b/src/types.ts index 5ec95eff..ae632ad8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,13 @@ import type { IHttpOperation, IHttpService } from '@stoplight/types'; export type Fragment = Record; +export type IdGenerator = (value: string) => string; + +export type RefResolver = ( + this: TransformerContext, + input: Fragment & { $ref: string }, +) => unknown; + export interface ITransformServiceOpts { document: T; } @@ -16,14 +23,20 @@ export interface ITransformOperationOpts { export type HttpOperationTransformer = (opts: T) => IHttpOperation; +export type ArrayCallbackParameters = [T, number, T[]]; + +export type AvailableContext = 'service' | 'path' | 'operation'; + export type TransformerContext = { - maybeResolveLocalRef(target: unknown): unknown; document: T; + context: AvailableContext; + parentId: string; + readonly ids: Record; + generateId(template: string): string; + maybeResolveLocalRef(target: unknown): unknown; }; export type TranslateFunction = ( this: TransformerContext, ...params: P ) => R; - -export type ArrayCallbackParameters = [T, number, T[]]; diff --git a/src/utils.ts b/src/utils.ts index 3d177946..39c3dee5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,21 +1,15 @@ -import { hasRef, isLocalRef, isPlainObject, resolveInlineRef } from '@stoplight/json'; - -export const maybeResolveLocalRef = (document: unknown, target: unknown): unknown => { - if (isPlainObject(document) && hasRef(target) && isLocalRef(target.$ref)) { - try { - return resolveInlineRef(document, target.$ref); - } catch { - return target; - } - } - - return target; -}; - -export { isPlainObject as isDictionary }; +import { isPlainObject } from '@stoplight/json'; +import isEqualWith = require('lodash.isequalwith'); export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; export function entries(o: T): [string, T][]; export function entries(o: T): [string, T][] { return isPlainObject(o) ? Object.entries(o as T) : []; } + +export function isEqual(left: unknown, right: unknown) { + return isEqualWith(left, right, (value, other, indexOrKey) => { + if (indexOrKey === 'id') return true; + return; + }); +} diff --git a/yarn.lock b/yarn.lock index 88d5e7d9..e8658755 100644 --- a/yarn.lock +++ b/yarn.lock @@ -83,9 +83,9 @@ "@babel/types" "^7.8.3" "@babel/highlight@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" - integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" @@ -1023,10 +1023,10 @@ dependencies: eslint-config-prettier "^8.3.0" -"@stoplight/json@^3.17.2": - version "3.17.2" - resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.17.2.tgz#b086322615f5b262e2bed1271511808fc8a04f4f" - integrity sha512-NwIVzanXRUy291J5BMkncCZRMG1Lx+aq+VidGQgfkJjgo8vh1Y/PSAz7fSU8gVGSZBCcqmOkMI7R4zw7DlfTwA== +"@stoplight/json@^3.18.0": + version "3.18.0" + resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.18.0.tgz#271ca5c7da5b8175b3b1e8344346a835aabc54a4" + integrity sha512-JeaOaee2dKOxz3gZKMfx+KxUi5pH7AU/TRgqnSEkQ+3FQhNgx6XYPZwvcfcsVOiHUa2tYIP3kRl30BgYWtSOow== dependencies: "@stoplight/ordered-object-literal" "^1.0.2" "@stoplight/types" "^12.3.0" @@ -1089,10 +1089,10 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" -"@stoplight/types@^13.0.0-beta.4": - version "13.0.0-beta.4" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.4.tgz#5ea5e93957b20a5effedcbd101e6e9cde7118e48" - integrity sha512-q9Dbyfi1DcZjTuDwptVLTCbeQgtpfTQAKQkbcrs64ayfvc7dPwO+81vYr0dSJsXzlFQktZv5M+eq0QPtJy/HGg== +"@stoplight/types@^13.0.0-beta.5": + version "13.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.5.tgz#31804f3d1e6369a6daeff985dd0ca7ab27637f70" + integrity sha512-qZgWB3QxoOJL2TsbSfPSyStsnH7BwvAljcT2xa0ZowP3/Eh16OYS/PWzQzTU/GZ+btyqpowGgTragLUgipQAjQ== dependencies: "@types/json-schema" "^7.0.4" utility-types "^3.10.0" @@ -1200,10 +1200,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/lodash.isequal@^4.5.5": - version "4.5.5" - resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz#4fed1b1b00bef79e305de0352d797e9bb816c8ff" - integrity sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg== +"@types/lodash.isequalwith@^4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@types/lodash.isequalwith/-/lodash.isequalwith-4.4.6.tgz#8c5dee2b2fdc05cfa79a3a77cd40cc8909ef79c9" + integrity sha512-55fjBOrhse+SLkqGlvUq1yQ/sDvsi93+ngLIG22DPEoeL4c/d1rtWMMK55pP4nKOjkSWv8ks1q0HaXXCgPr81w== dependencies: "@types/lodash" "*" @@ -1295,13 +1295,13 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.15.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.16.0.tgz#78f246dd8d1b528fc5bfca99a8a64d4023a3d86d" - integrity sha512-SJoba1edXvQRMmNI505Uo4XmGbxCK9ARQpkvOd00anxzri9RNQk0DDCxD+LIl+jYhkzOJiOMMKYEHnHEODjdCw== + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz#c28ef7f2e688066db0b6a9d95fb74185c114fb9a" + integrity sha512-u6Db5JfF0Esn3tiAKELvoU5TpXVSkOpZ78cEGn/wXtT2RVqs2vkt4ge6N8cRCyw7YVKhmmLDbwI2pg92mlv7cA== dependencies: - "@typescript-eslint/scope-manager" "5.16.0" - "@typescript-eslint/type-utils" "5.16.0" - "@typescript-eslint/utils" "5.16.0" + "@typescript-eslint/scope-manager" "5.15.0" + "@typescript-eslint/type-utils" "5.15.0" + "@typescript-eslint/utils" "5.15.0" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -1310,68 +1310,68 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.15.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.16.0.tgz#e4de1bde4b4dad5b6124d3da227347616ed55508" - integrity sha512-fkDq86F0zl8FicnJtdXakFs4lnuebH6ZADDw6CYQv0UZeIjHvmEw87m9/29nk2Dv5Lmdp0zQ3zDQhiMWQf/GbA== + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.15.0.tgz#95f603f8fe6eca7952a99bfeef9b85992972e728" + integrity sha512-NGAYP/+RDM2sVfmKiKOCgJYPstAO40vPAgACoWPO/+yoYKSgAXIFaBKsV8P0Cc7fwKgvj27SjRNX4L7f4/jCKQ== dependencies: - "@typescript-eslint/scope-manager" "5.16.0" - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/typescript-estree" "5.16.0" + "@typescript-eslint/scope-manager" "5.15.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/typescript-estree" "5.15.0" debug "^4.3.2" -"@typescript-eslint/scope-manager@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.16.0.tgz#7e7909d64bd0c4d8aef629cdc764b9d3e1d3a69a" - integrity sha512-P+Yab2Hovg8NekLIR/mOElCDPyGgFZKhGoZA901Yax6WR6HVeGLbsqJkZ+Cvk5nts/dAlFKm8PfL43UZnWdpIQ== +"@typescript-eslint/scope-manager@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz#d97afab5e0abf4018d1289bd711be21676cdd0ee" + integrity sha512-EFiZcSKrHh4kWk0pZaa+YNJosvKE50EnmN4IfgjkA3bTHElPtYcd2U37QQkNTqwMCS7LXeDeZzEqnsOH8chjSg== dependencies: - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/visitor-keys" "5.16.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/visitor-keys" "5.15.0" -"@typescript-eslint/type-utils@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.16.0.tgz#b482bdde1d7d7c0c7080f7f2f67ea9580b9e0692" - integrity sha512-SKygICv54CCRl1Vq5ewwQUJV/8padIWvPgCxlWPGO/OgQLCijY9G7lDu6H+mqfQtbzDNlVjzVWQmeqbLMBLEwQ== +"@typescript-eslint/type-utils@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz#d2c02eb2bdf54d0a645ba3a173ceda78346cf248" + integrity sha512-KGeDoEQ7gHieLydujGEFLyLofipe9PIzfvA/41urz4hv+xVxPEbmMQonKSynZ0Ks2xDhJQ4VYjB3DnRiywvKDA== dependencies: - "@typescript-eslint/utils" "5.16.0" + "@typescript-eslint/utils" "5.15.0" debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/types@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.16.0.tgz#5827b011982950ed350f075eaecb7f47d3c643ee" - integrity sha512-oUorOwLj/3/3p/HFwrp6m/J2VfbLC8gjW5X3awpQJ/bSG+YRGFS4dpsvtQ8T2VNveV+LflQHjlLvB6v0R87z4g== +"@typescript-eslint/types@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.15.0.tgz#c7bdd103843b1abae97b5518219d3e2a0d79a501" + integrity sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA== -"@typescript-eslint/typescript-estree@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.16.0.tgz#32259459ec62f5feddca66adc695342f30101f61" - integrity sha512-SE4VfbLWUZl9MR+ngLSARptUv2E8brY0luCdgmUevU6arZRY/KxYoLI/3V/yxaURR8tLRN7bmZtJdgmzLHI6pQ== +"@typescript-eslint/typescript-estree@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz#81513a742a9c657587ad1ddbca88e76c6efb0aac" + integrity sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA== dependencies: - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/visitor-keys" "5.16.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/visitor-keys" "5.15.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/utils@5.16.0", "@typescript-eslint/utils@^5.10.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.16.0.tgz#42218b459d6d66418a4eb199a382bdc261650679" - integrity sha512-iYej2ER6AwmejLWMWzJIHy3nPJeGDuCqf8Jnb+jAQVoPpmWzwQOfa9hWVB8GIQE5gsCv/rfN4T+AYb/V06WseQ== +"@typescript-eslint/utils@5.15.0", "@typescript-eslint/utils@^5.10.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.15.0.tgz#468510a0974d3ced8342f37e6c662778c277f136" + integrity sha512-081rWu2IPKOgTOhHUk/QfxuFog8m4wxW43sXNOMSCdh578tGJ1PAaWPsj42LOa7pguh173tNlMigsbrHvh/mtA== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.16.0" - "@typescript-eslint/types" "5.16.0" - "@typescript-eslint/typescript-estree" "5.16.0" + "@typescript-eslint/scope-manager" "5.15.0" + "@typescript-eslint/types" "5.15.0" + "@typescript-eslint/typescript-estree" "5.15.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.16.0": - version "5.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.16.0.tgz#f27dc3b943e6317264c7492e390c6844cd4efbbb" - integrity sha512-jqxO8msp5vZDhikTwq9ubyMHqZ67UIvawohr4qF3KhlpL7gzSjOd+8471H3nh5LyABkaI85laEKKU8SnGUK5/g== +"@typescript-eslint/visitor-keys@5.15.0": + version "5.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz#5669739fbf516df060f978be6a6dce75855a8027" + integrity sha512-+vX5FKtgvyHbmIJdxMJ2jKm9z2BIlXJiuewI8dsDYMp5LzPUcuTT78Ya5iwvQg3VqSVdmxyM8Anj1Jeq7733ZQ== dependencies: - "@typescript-eslint/types" "5.16.0" + "@typescript-eslint/types" "5.15.0" eslint-visitor-keys "^3.0.0" JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: @@ -1411,9 +1411,9 @@ acorn-walk@^7.1.1: integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + version "7.3.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" + integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" @@ -3158,9 +3158,9 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.12.0" eslint-plugin-jest@^26.1.1: - version "26.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.2.tgz#0f1a15c62889fffc3f78a773749d672f1bedb15f" - integrity sha512-1bXCoRODPkGN06n9KAMls4Jm0eyS+0Q/LWcIxhqWR2ycV0Z7lnx2c10idk4dtFIJY5xStgiIr5snC6/rxcXpbw== + version "26.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-26.1.1.tgz#7176dd745ef8bca3070263f62cdf112f2dfc9aa1" + integrity sha512-HRKOuPi5ADhza4ZBK5ufyNXy28bXXkib87w+pQqdvBhSTsamndh6sIAKPAUl8y0/n9jSWBdTPslrwtKWqkp8dA== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -3683,6 +3683,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +fnv-plus@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/fnv-plus/-/fnv-plus-1.3.1.tgz#c34cb4572565434acb08ba257e4044ce2b006d67" + integrity sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4089,7 +4094,7 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -5826,10 +5831,10 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.isequalwith@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0" + integrity sha1-Jmcm3dUo+FTyH06pigZWBuD7xrA= lodash.ismatch@^4.4.0: version "4.4.0" @@ -6200,10 +6205,10 @@ micromatch@^3.0.4, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +mime-db@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== mime-format@2.0.1: version "2.0.1" @@ -6212,12 +6217,12 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.34, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== +mime-types@2.1.32, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== dependencies: - mime-db "1.51.0" + mime-db "1.49.0" mime@^2.4.3: version "2.4.4" @@ -6254,10 +6259,10 @@ minimist@1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minimist@~0.0.1: version "0.0.10" @@ -7284,9 +7289,9 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postman-collection@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.1.tgz#aba50dfca3c3ce6881b9598958c6a52eec3959d9" - integrity sha512-ODpJtlf8r99DMcTU7gFmi/yvQYckFzcuE6zL/fWnyrFT34ugdCBFlX+DN7M+AnP6lmR822fv5s60H4DnL4+fAg== + version "4.1.0" + resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.0.tgz#c5833aa3cb82df79cc5d16e5d7399c71a84ea4fa" + integrity sha512-J9IpCMXpGDLN7MGhdMcUbZ0SIWLCcTVdrjTgKVYubkW1sn1KcDqJgsdTr/ItkO8dOXKLuhvnq2QnE5Vrzb3WMA== dependencies: faker "5.5.3" file-type "3.9.0" @@ -7295,7 +7300,7 @@ postman-collection@^4.1.0: liquid-json "0.3.1" lodash "4.17.21" mime-format "2.0.1" - mime-types "2.1.34" + mime-types "2.1.32" postman-url-encoder "3.0.5" semver "7.3.5" uuid "8.3.2" @@ -7698,7 +7703,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.1: +regexp.prototype.flags@^1.3.1: version "1.4.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== @@ -8530,17 +8535,17 @@ string-width@^4.1.0, string-width@^4.2.0: strip-ansi "^6.0.0" string.prototype.matchall@^4.0.6: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== + version "4.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" + integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.19.1" get-intrinsic "^1.1.1" - has-symbols "^1.0.3" + has-symbols "^1.0.2" internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" + regexp.prototype.flags "^1.3.1" side-channel "^1.0.4" string.prototype.trimend@^1.0.4: @@ -8947,13 +8952,13 @@ ts-node@^8.10.2: yn "3.1.1" tsconfig-paths@^3.12.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.14.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz#4fcc48f9ccea8826c41b9ca093479de7f5018976" + integrity sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.6" + minimist "^1.2.0" strip-bom "^3.0.0" tslib@2.1.0: From 77f61f3b2a7a2c64c26dfb3648efba62efe88ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Wed, 30 Mar 2022 12:40:52 +0200 Subject: [PATCH 11/16] fix: improve entry points --- src/index.ts | 1 + src/oas/index.ts | 1 + src/oas/transformers/index.ts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index cfc451c5..791438c5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from './oas'; export * from './oas2'; export * from './oas3'; export * from './postman'; +export * from './types'; diff --git a/src/oas/index.ts b/src/oas/index.ts index 633a1ce4..aa9ce9c3 100644 --- a/src/oas/index.ts +++ b/src/oas/index.ts @@ -1,2 +1,3 @@ export * from './operation'; +export { convertSchema as convertToJsonSchema } from './transformers/index'; export * from './types'; diff --git a/src/oas/transformers/index.ts b/src/oas/transformers/index.ts index 452614f3..1a495f35 100644 --- a/src/oas/transformers/index.ts +++ b/src/oas/transformers/index.ts @@ -1 +1,2 @@ -export { translateSchemaObject } from './schema/index'; +export { convertSchema, translateSchemaObject } from './schema/index'; +export { translateLogo } from './translateLogo'; From ab269d54d83bf5db474a0552f1621c5ea3efc98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Wed, 30 Mar 2022 12:53:46 +0200 Subject: [PATCH 12/16] fix(oas): support jsonSchemaDialect in convertSchema --- src/oas/transformers/schema/index.ts | 24 +++++++++++------------- src/postman/transformers/params.ts | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/oas/transformers/schema/index.ts b/src/oas/transformers/schema/index.ts index 3d5b2bea..1d8a9b80 100644 --- a/src/oas/transformers/schema/index.ts +++ b/src/oas/transformers/schema/index.ts @@ -5,7 +5,7 @@ import type { OpenAPIObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; import { withContext } from '../../../context'; -import type { TranslateFunction } from '../../../types'; +import type { Fragment, TranslateFunction } from '../../../types'; import { getSharedKey } from '../../resolver'; import keywords from './keywords'; import type { OASSchemaObject } from './types'; @@ -27,7 +27,6 @@ export const translateSchemaObject = withContext< JSONSchema7 > >(function (schema) { - const document = this.document; const resolvedSchema = this.maybeResolveLocalRef(schema); if (!isPlainObject(resolvedSchema)) return {}; let cached = CACHE.get(resolvedSchema); @@ -38,24 +37,23 @@ export const translateSchemaObject = withContext< const actualKey = this.context === 'service' ? getSharedKey(resolvedSchema) : ''; const id = this.generateId(`schema-${this.parentId}-${actualKey}`); + cached = convertSchema(this.document, resolvedSchema); + cached['x-stoplight-id'] = id; + + CACHE.set(resolvedSchema, cached); + return cached; +}); + +export function convertSchema(document: Fragment, schema: OASSchemaObject) { if ('jsonSchemaDialect' in document && typeof document.jsonSchemaDialect === 'string') { - cached = { + return { $schema: document.jsonSchemaDialect, // let's assume it's draft 7, albeit it might be draft 2020-12 or 2019-09. // it's a safe bet, because there was only _one_ relatively minor breaking change introduced between Draft 7 and 2020-12. - ...(resolvedSchema as JSONSchema7), - 'x-stoplight-id': id, + ...(schema as JSONSchema7), }; - } else { - cached = convertSchema(resolvedSchema); - cached['x-stoplight-id'] = id; } - CACHE.set(resolvedSchema, cached); - return cached; -}); - -export function convertSchema(schema: OASSchemaObject): JSONSchema7 { const clonedSchema = _convertSchema(schema, { structs: ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'additionalItems'], }); diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 96370a5c..0bc1b8f5 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -90,7 +90,7 @@ export function transformRawBody(raw: string, mediaType: string = 'text/plain'): value: parsed, }, ], - schema: convertSchema(jsonSchemaGenerator(parsed)), + schema: convertSchema({}, jsonSchemaGenerator(parsed)), }; } catch (e) { /* noop, move on.. */ From 4296f84d8baf0827c40507859c09bfbf81749766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Wed, 30 Mar 2022 22:31:16 +0200 Subject: [PATCH 13/16] fix: expose operation in package exports to preserve comp with prism-cli --- package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package.json b/package.json index a1329423..97afbf26 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,25 @@ "types": "./oas2/index.d.ts", "default": "./oas2/index.js" }, + "./oas2/operation": { + "types": "./oas2/operation/index.d.ts", + "default": "./oas2/operation/index.js" + }, "./oas3": { "types": "./oas3/index.d.ts", "default": "./oas3/index.js" }, + "./oas3/operation": { + "types": "./oas3/operation/index.d.ts", + "default": "./oas3/operation/index.js" + }, "./postman": { "types": "./postman/index.d.ts", "default": "./postman/index.js" + }, + "./postman/operation": { + "types": "./postman/operation/index.d.ts", + "default": "./postman/operation/index.js" } }, "scripts": { From 5a95b7ad4dfa7be87406fcff619d162cbd10cd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Thu, 31 Mar 2022 00:39:37 +0200 Subject: [PATCH 14/16] fix: incorrect entries for operations --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 97afbf26..a3094731 100644 --- a/package.json +++ b/package.json @@ -35,24 +35,24 @@ "default": "./oas2/index.js" }, "./oas2/operation": { - "types": "./oas2/operation/index.d.ts", - "default": "./oas2/operation/index.js" + "types": "./oas2/operation.d.ts", + "default": "./oas2/operation.js" }, "./oas3": { "types": "./oas3/index.d.ts", "default": "./oas3/index.js" }, "./oas3/operation": { - "types": "./oas3/operation/index.d.ts", - "default": "./oas3/operation/index.js" + "types": "./oas3/operation.d.ts", + "default": "./oas3/operation.js" }, "./postman": { "types": "./postman/index.d.ts", "default": "./postman/index.js" }, "./postman/operation": { - "types": "./postman/operation/index.d.ts", - "default": "./postman/operation/index.js" + "types": "./postman/operation.d.ts", + "default": "./postman/operation.js" } }, "scripts": { From 7a9a87ea4529c679a6c9be0bcc9819be912ee48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Tue, 5 Apr 2022 12:57:42 +0200 Subject: [PATCH 15/16] fix(oas2): accept x-stoplight-id on document --- src/oas/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas/types.ts b/src/oas/types.ts index 884fa22d..f9b37827 100644 --- a/src/oas/types.ts +++ b/src/oas/types.ts @@ -17,7 +17,7 @@ export enum OasVersion { /** * Service */ -export type Oas2TransformServiceOpts = ITransformServiceOpts>; +export type Oas2TransformServiceOpts = ITransformServiceOpts>; export type Oas3TransformServiceOpts = ITransformServiceOpts>; export type Oas2HttpServiceTransformer = HttpServiceTransformer; export type Oas3HttpServiceTransformer = HttpServiceTransformer; From 63c956e4eae16eaea3d33b2c82b0e1f57b2683d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ro=C5=BCek?= Date: Fri, 14 Jan 2022 02:51:15 +0200 Subject: [PATCH 16/16] feat: dual packaging --- jest.config.js | 10 +- package.json | 34 +- rollup.config.js | 36 + src/context.ts | 2 +- src/oas/__tests__/operation.test.ts | 13 +- src/oas/__tests__/service.test.ts | 21 +- src/oas/operation.ts | 2 +- src/oas/service.ts | 2 +- src/oas/tags.ts | 2 +- src/oas/transformers/security.ts | 0 src/oas/transformers/translateLogo.ts | 2 +- src/oas2/accessors.ts | 2 +- src/oas2/service.ts | 2 +- .../__snapshots__/request.test.ts.snap | 120 +- .../transformers/__tests__/params.test.ts | 3 - .../transformers/__tests__/request.test.ts | 167 +- src/oas2/transformers/content.ts | 0 src/oas2/transformers/params.ts | 25 +- src/oas2/transformers/request.ts | 4 +- src/oas2/transformers/responses.ts | 2 +- src/oas2/transformers/securities.ts | 2 +- src/oas2/transformers/servers.ts | 3 +- src/oas3/__tests__/ids.test.ts | 5 +- src/oas3/accessors.ts | 2 +- src/oas3/operation.ts | 2 +- src/oas3/service.ts | 2 +- .../transformers/__tests__/content.test.ts | 4 - src/oas3/transformers/content.ts | 2 +- src/oas3/transformers/examples.ts | 2 +- src/oas3/transformers/request.ts | 2 +- src/oas3/transformers/responses.ts | 2 +- src/oas3/transformers/servers.ts | 2 +- src/postman/__tests__/util.test.ts | 4 +- .../transformers/__tests__/params.test.ts | 4 +- .../transformers/__tests__/request.test.ts | 4 +- .../transformers/__tests__/response.test.ts | 4 +- .../__tests__/securitySchemes.spec.ts | 5 +- .../transformers/__tests__/server.test.ts | 4 +- src/postman/transformers/params.ts | 2 +- src/postman/util.ts | 7 +- src/utils.ts | 2 +- tsconfig.build.json | 3 +- tsconfig.json | 6 +- yarn.lock | 1865 ++++++++++------- 44 files changed, 1521 insertions(+), 868 deletions(-) create mode 100644 rollup.config.js delete mode 100644 src/oas/transformers/security.ts delete mode 100644 src/oas2/transformers/content.ts diff --git a/jest.config.js b/jest.config.js index a9426838..b93b4ef9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,13 @@ module.exports = { - preset: 'ts-jest', + preset: 'ts-jest/presets/default-esm', testEnvironment: 'node', testMatch: ['/src/**/__tests__/*.(spec|test).(ts|js)'], + globals: { + 'ts-jest': { + useESM: true, + }, + }, + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, }; diff --git a/package.json b/package.json index a3094731..31af8295 100644 --- a/package.json +++ b/package.json @@ -20,43 +20,53 @@ }, "type": "commonjs", "main": "./index.js", + "module": "./index.esm.js", "types": "./index.d.ts", "exports": { ".": { "types": "./index.d.ts", - "default": "./index.js" + "import": "./index.mjs", + "require": "./index.js" }, "./oas": { "types": "./oas/index.d.ts", - "default": "./oas/index.js" + "import": "./oas/index.mjs", + "require": "./oas/index.js" }, "./oas2": { "types": "./oas2/index.d.ts", - "default": "./oas2/index.js" + "import": "./oas2/index.mjs", + "require": "./oas2/index.js" }, "./oas2/operation": { "types": "./oas2/operation.d.ts", - "default": "./oas2/operation.js" + "import": "./oas2/operation.mjs", + "require": "./oas2/operation.js" }, "./oas3": { "types": "./oas3/index.d.ts", - "default": "./oas3/index.js" + "import": "./oas3/index.mjs", + "require": "./oas3/index.js" }, "./oas3/operation": { "types": "./oas3/operation.d.ts", - "default": "./oas3/operation.js" + "import": "./oas3/operation.mjs", + "require": "./oas3/operation.js" }, "./postman": { "types": "./postman/index.d.ts", - "default": "./postman/index.js" + "import": "./postman/index.mjs", + "require": "./postman/index.js" }, "./postman/operation": { "types": "./postman/operation.d.ts", - "default": "./postman/operation.js" + "import": "./postman/operation.mjs", + "require": "./postman/operation.js" } }, "scripts": { - "build": "sl-scripts build", + "build": "sl-scripts bundle && cp ./dist/index.mjs ./dist/index.esm.js && tsc -p tsconfig.build.json --declaration --emitDeclarationOnly --declarationDir dist", + "postbuild": "yarn test.packaging", "lint": "eslint 'src/**/*.ts'", "lint.fix": "yarn lint --fix", "release": "sl-scripts release", @@ -65,7 +75,7 @@ }, "dependencies": { "@stoplight/json": "^3.18.0", - "@stoplight/types": "^13.0.0-beta.5", + "@stoplight/types": "^13.0.0", "@types/json-schema": "7.0.5", "@types/swagger-schema-official": "~2.0.21", "@types/type-is": "^1.6.3", @@ -97,9 +107,9 @@ "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-simple-import-sort": "^7.0.0", - "jest": "26.1.0", + "jest": "^27.5.1", "prettier": "^2.6.0", - "ts-jest": "26.1.1", + "ts-jest": "^27.1.4", "ts-node": "^8.10.2", "typescript": "4.6.2", "utility-types": "^3.10.0" diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..341004ee --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,36 @@ +import * as path from 'path'; +import typescript from 'rollup-plugin-typescript2'; + +import pkg from './package.json'; + +module.exports = { + input: Object.values(pkg.exports).map(({ require: entry }) => + path.join('src', path.dirname(entry), path.basename(entry, path.extname(entry)) + '.ts'), + ), + output: [ + { + dir: './dist', + entryFileNames: '[name].js', + exports: 'named', + format: 'cjs', + name: pkg.name, + preserveModules: true, + sourcemap: true, + }, + { + dir: './dist', + entryFileNames: '[name].mjs', + format: 'es', + name: pkg.name, + preserveModules: true, + sourcemap: true, + }, + ], + external: Object.keys(pkg.dependencies), + plugins: [ + typescript({ + tsconfig: './tsconfig.build.json', + useTsconfigDeclarationDir: true, + }), + ], +}; diff --git a/src/context.ts b/src/context.ts index 0a4c3334..43b4bc97 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,6 +1,6 @@ import { hasRef, isLocalRef } from '@stoplight/json'; // @ts-ignore -import * as fnv from 'fnv-plus'; +import fnv from 'fnv-plus'; import type { AvailableContext, diff --git a/src/oas/__tests__/operation.test.ts b/src/oas/__tests__/operation.test.ts index 402f32c5..5605ab79 100644 --- a/src/oas/__tests__/operation.test.ts +++ b/src/oas/__tests__/operation.test.ts @@ -1,11 +1,16 @@ -import { OpenAPIObject } from 'openapi3-ts'; -import { Spec } from 'swagger-schema-official'; +import * as fs from 'fs'; +import * as path from 'path'; +import { fileURLToPath } from 'url'; import { transformOas2Operations } from '../../oas2/operation'; import { transformOas3Operations } from '../../oas3/operation'; -const oas2KitchenSinkJson: Spec = require('./fixtures/oas2-kitchen-sink.json'); -const oas3KitchenSinkJson: OpenAPIObject = require('./fixtures/oas3-kitchen-sink.json'); +const oas2KitchenSinkJson = JSON.parse( + await fs.promises.readFile(path.join(fileURLToPath(import.meta.url), '../fixtures/oas2-kitchen-sink.json'), 'utf8'), +); +const oas3KitchenSinkJson = JSON.parse( + await fs.promises.readFile(path.join(fileURLToPath(import.meta.url), '../fixtures/oas3-kitchen-sink.json'), 'utf8'), +); describe('oas operation', () => { it('openapi v2', () => { diff --git a/src/oas/__tests__/service.test.ts b/src/oas/__tests__/service.test.ts index 44471478..4bf89c9c 100644 --- a/src/oas/__tests__/service.test.ts +++ b/src/oas/__tests__/service.test.ts @@ -1,20 +1,29 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { fileURLToPath } from 'url'; + import { transformOas2Service } from '../../oas2/service'; import { transformOas3Service } from '../../oas3/service'; -import * as oas2KitchenSinkJson from './fixtures/oas2-kitchen-sink.json'; -import * as oas3KitchenSinkJson from './fixtures/oas3-kitchen-sink.json'; + +const oas2KitchenSinkJson = JSON.parse( + await fs.promises.readFile(path.join(fileURLToPath(import.meta.url), '../fixtures/oas2-kitchen-sink.json'), 'utf8'), +); +const oas3KitchenSinkJson = JSON.parse( + await fs.promises.readFile(path.join(fileURLToPath(import.meta.url), '../fixtures/oas3-kitchen-sink.json'), 'utf8'), +); describe('oas service', () => { it('openapi v2', () => { - expect(transformOas2Service({ document: oas2KitchenSinkJson as any })).toMatchSnapshot(); + expect(transformOas2Service({ document: oas2KitchenSinkJson })).toMatchSnapshot(); }); it('openapi v3', () => { - expect(transformOas3Service({ document: oas3KitchenSinkJson as any })).toMatchSnapshot(); + expect(transformOas3Service({ document: oas3KitchenSinkJson })).toMatchSnapshot(); }); it('openapi v2 and v3 should transform in the same way', () => { - const oas2MappedService = transformOas2Service({ document: oas2KitchenSinkJson as any }); - const oas3MappedService = transformOas3Service({ document: oas3KitchenSinkJson as any }); + const oas2MappedService = transformOas2Service({ document: oas2KitchenSinkJson }); + const oas3MappedService = transformOas3Service({ document: oas3KitchenSinkJson }); expect(oas2MappedService).toStrictEqual(oas3MappedService); }); }); diff --git a/src/oas/operation.ts b/src/oas/operation.ts index 8f8aa763..efa6a188 100644 --- a/src/oas/operation.ts +++ b/src/oas/operation.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, IHttpOperation } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import type { OpenAPIObject, OperationObject, PathsObject } from 'openapi3-ts'; import type { Spec } from 'swagger-schema-official'; diff --git a/src/oas/service.ts b/src/oas/service.ts index c8fc8b81..b28f0c25 100644 --- a/src/oas/service.ts +++ b/src/oas/service.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import { DeepPartial, IHttpService } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { OpenAPIObject } from 'openapi3-ts'; import { Spec } from 'swagger-schema-official'; diff --git a/src/oas/tags.ts b/src/oas/tags.ts index 573ca598..641b97cb 100644 --- a/src/oas/tags.ts +++ b/src/oas/tags.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import type { INodeTag, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { withContext } from '../context'; import { isNonNullable, isSerializablePrimitive, isString } from '../guards'; diff --git a/src/oas/transformers/security.ts b/src/oas/transformers/security.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/oas/transformers/translateLogo.ts b/src/oas/transformers/translateLogo.ts index 2e5da953..e05529d0 100644 --- a/src/oas/transformers/translateLogo.ts +++ b/src/oas/transformers/translateLogo.ts @@ -1,7 +1,7 @@ import type { DeepPartial, Dictionary, IHttpService } from '@stoplight/types'; +import pickBy from 'lodash.pickby'; import type { InfoObject } from 'openapi3-ts/src/model/OpenApi'; import type { Info } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); export function translateLogo({ 'x-logo': logo, diff --git a/src/oas2/accessors.ts b/src/oas2/accessors.ts index 1230a42b..06c85878 100644 --- a/src/oas2/accessors.ts +++ b/src/oas2/accessors.ts @@ -1,7 +1,7 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial } from '@stoplight/types'; +import pickBy from 'lodash.pickby'; import type { Operation, Security, Spec } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); import { isNonNullable, isString } from '../guards'; import { isSecurityScheme } from './guards'; diff --git a/src/oas2/service.ts b/src/oas2/service.ts index 344282f5..7c4c2ced 100644 --- a/src/oas2/service.ts +++ b/src/oas2/service.ts @@ -1,5 +1,5 @@ import { isPlainObject } from '@stoplight/json'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { createContext, DEFAULT_ID_GENERATOR } from '../context'; import { isNonNullable, isString } from '../guards'; diff --git a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap index c496d16a..0c85547b 100644 --- a/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap +++ b/src/oas2/transformers/__tests__/__snapshots__/request.test.ts.snap @@ -3,8 +3,14 @@ exports[`request given single body param should translate to request with body 1`] = ` Object { "body": Object { - "in": "body", - "name": "param", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "*", + }, + ], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -16,9 +22,23 @@ Object { exports[`request given single form param should translate to request with form 1`] = ` Object { "body": Object { - "in": "formData", - "name": "param", - "type": "number", + "contents": Array [ + Object { + "id": Any, + "mediaType": "*", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": Object { + "param": Object { + "type": "number", + }, + }, + "type": "object", + "x-stoplight-id": Any, + }, + }, + ], + "id": Any, }, "cookie": Array [], "headers": Array [], @@ -32,8 +52,13 @@ Object { "cookie": Array [], "headers": Array [ Object { - "in": "header", + "id": Any, "name": "param", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "simple", }, ], "path": Array [], @@ -47,9 +72,14 @@ Object { "headers": Array [], "path": Array [ Object { - "in": "path", + "id": Any, "name": "param", "required": true, + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "simple", }, ], "query": Array [], @@ -63,8 +93,13 @@ Object { "path": Array [], "query": Array [ Object { - "in": "query", + "id": Any, "name": "param", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "form", }, ], } @@ -75,8 +110,13 @@ Object { "cookie": Array [], "headers": Array [ Object { - "in": "header", + "id": Any, "name": "param", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "simple", }, ], "path": Array [], @@ -90,9 +130,14 @@ Object { "headers": Array [], "path": Array [ Object { - "in": "path", + "id": Any, "name": "param", "required": true, + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "simple", }, ], "query": Array [], @@ -106,8 +151,13 @@ Object { "path": Array [], "query": Array [ Object { - "in": "query", + "id": Any, "name": "param", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "form", }, ], } @@ -116,13 +166,49 @@ Object { exports[`request should translate mixed request 1`] = ` Object { "body": Object { - "in": "formData", - "name": "param", - "type": "number", + "contents": Array [ + Object { + "examples": Array [], + "id": Any, + "mediaType": "*", + }, + ], + "id": Any, }, "cookie": Array [], - "headers": Array [], - "path": Array [], - "query": Array [], + "headers": Array [ + Object { + "id": Any, + "name": "param", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "simple", + }, + ], + "path": Array [ + Object { + "id": Any, + "name": "param", + "required": true, + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "simple", + }, + ], + "query": Array [ + Object { + "id": Any, + "name": "param", + "schema": Object { + "$schema": "http://json-schema.org/draft-07/schema#", + "x-stoplight-id": Any, + }, + "style": "form", + }, + ], } `; diff --git a/src/oas2/transformers/__tests__/params.test.ts b/src/oas2/transformers/__tests__/params.test.ts index 518d59b1..1b4f564c 100644 --- a/src/oas2/transformers/__tests__/params.test.ts +++ b/src/oas2/transformers/__tests__/params.test.ts @@ -337,7 +337,6 @@ describe('params.translator', () => { $schema: 'http://json-schema.org/draft-07/schema#', properties: { arr: { - 'x-stoplight-id': expect.any(String), description: 'desc', items: { type: 'number', @@ -347,14 +346,12 @@ describe('params.translator', () => { type: 'array', }, int: { - 'x-stoplight-id': expect.any(String), description: 'desc', maximum: 3, minimum: 0, type: 'integer', }, str: { - 'x-stoplight-id': expect.any(String), minLength: 1, default: '25-07-2019', description: 'desc', diff --git a/src/oas2/transformers/__tests__/request.test.ts b/src/oas2/transformers/__tests__/request.test.ts index a79c060d..3427cb50 100644 --- a/src/oas2/transformers/__tests__/request.test.ts +++ b/src/oas2/transformers/__tests__/request.test.ts @@ -8,81 +8,170 @@ import { import { createContext, DEFAULT_ID_GENERATOR } from '../../../context'; import { resolveRef } from '../../../oas/resolver'; -import { - translateFromFormDataParameters, - translateToBodyParameter, - translateToHeaderParam, - translateToPathParameter, - translateToQueryParameter, -} from '../params'; import { translateToRequest as _translateToRequest } from '../request'; -jest.mock('../params'); - -const translateToRequest = (path: Record, parameters: any[]) => { - const ctx = createContext({ consumes: ['*'], paths: { '/api': { parameters } } }, resolveRef, DEFAULT_ID_GENERATOR); - return _translateToRequest.call(ctx, path, { parameters }); +const translateToRequest = (parameters: any[]) => { + const document = { consumes: ['*'], paths: { '/api': { get: { parameters } } } }; + const ctx = createContext(document, resolveRef, DEFAULT_ID_GENERATOR); + return _translateToRequest.call(ctx, document.paths['/api'], document.paths['/api'].get); }; describe('request', () => { - const fakeParameter: FormDataParameter = { - name: 'name', - type: 'string', - in: 'formData', - }; const fakeBodyParameter: BodyParameter = { in: 'body', name: 'param' }; const fakeFormParameter: FormDataParameter = { in: 'formData', name: 'param', type: 'number' }; const fakeQueryParameter: QueryParameter = { in: 'query', name: 'param' }; const fakeHeaderParameter: HeaderParameter = { in: 'header', name: 'param' }; const fakePathParameter: PathParameter = { in: 'path', name: 'param', required: true }; - beforeEach(() => { - (translateToBodyParameter as jest.Mock).mockReturnValue(fakeBodyParameter); - (translateFromFormDataParameters as jest.Mock).mockReturnValue(fakeFormParameter); - (translateToQueryParameter as jest.Mock).mockReturnValue(fakeQueryParameter); - (translateToPathParameter as jest.Mock).mockReturnValue(fakePathParameter); - (translateToHeaderParam as jest.Mock).mockReturnValue(fakeHeaderParameter); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - it('given single body param should translate to request with body', () => { - expect(translateToRequest({}, [fakeBodyParameter])).toMatchSnapshot(); + expect(translateToRequest([fakeBodyParameter])).toMatchSnapshot({ + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + }, + ], + }, + }); }); it('given single form param should translate to request with form', () => { - expect(translateToRequest({}, [fakeFormParameter])).toMatchSnapshot(); + expect(translateToRequest([fakeFormParameter])).toMatchSnapshot({ + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }, + }); }); it('given single path param should translate to request with path', () => { - expect(translateToRequest({}, [fakePathParameter])).toMatchSnapshot(); + expect(translateToRequest([fakePathParameter])).toMatchSnapshot({ + path: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given single query param should translate to request with query', () => { - expect(translateToRequest({}, [fakeQueryParameter])).toMatchSnapshot(); + expect(translateToRequest([fakeQueryParameter])).toMatchSnapshot({ + query: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given single header param should translate to request with header', () => { - expect(translateToRequest({}, [fakeHeaderParameter])).toMatchSnapshot(); + expect(translateToRequest([fakeHeaderParameter])).toMatchSnapshot({ + headers: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given two query params should translate', () => { - expect(translateToRequest({}, [fakeQueryParameter, fakeQueryParameter])).toMatchSnapshot(); + expect(translateToRequest([fakeQueryParameter, fakeQueryParameter])).toMatchSnapshot({ + query: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given two header params should translate', () => { - expect(translateToRequest({}, [fakeHeaderParameter, fakeHeaderParameter])).toMatchSnapshot(); + expect(translateToRequest([fakeHeaderParameter, fakeHeaderParameter])).toMatchSnapshot({ + headers: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('given two path params should translate', () => { - expect(translateToRequest({}, [fakePathParameter, fakePathParameter])).toMatchSnapshot(); + expect(translateToRequest([fakePathParameter, fakePathParameter])).toMatchSnapshot({ + path: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); it('should translate mixed request', () => { expect( - translateToRequest({}, [fakeParameter, fakeParameter, fakeParameter, fakeParameter, fakeParameter]), - ).toMatchSnapshot(); + translateToRequest([ + fakeBodyParameter, + fakeQueryParameter, + fakePathParameter, + fakeHeaderParameter, + fakeFormParameter, + ]), + ).toMatchSnapshot({ + body: { + id: expect.any(String), + contents: [ + { + id: expect.any(String), + }, + ], + }, + headers: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + path: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + query: [ + { + id: expect.any(String), + schema: { + 'x-stoplight-id': expect.any(String), + }, + }, + ], + }); }); }); diff --git a/src/oas2/transformers/content.ts b/src/oas2/transformers/content.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/oas2/transformers/params.ts b/src/oas2/transformers/params.ts index 894c3520..88b50483 100644 --- a/src/oas2/transformers/params.ts +++ b/src/oas2/transformers/params.ts @@ -10,6 +10,8 @@ import { Optional, } from '@stoplight/types'; import type { JSONSchema7 } from 'json-schema'; +import pick from 'lodash.pick'; +import pickBy from 'lodash.pickby'; import type { BodyParameter, FormDataParameter, @@ -18,8 +20,6 @@ import type { PathParameter, QueryParameter, } from 'swagger-schema-official'; -import pickBy = require('lodash.pickby'); -import pick = require('lodash.pick'); import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; @@ -114,8 +114,14 @@ export const translateToBodyParameter = withContext< return { id: this.generateId(`http_media-${this.parentId}-${mediaType}`), mediaType, - schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, examples, + + ...pickBy( + { + schema: isPlainObject(body.schema) ? translateSchemaObject.call(this, body.schema) : void 0, + }, + isNonNullable, + ), }; }), this, @@ -157,10 +163,11 @@ export const translateFromFormDataParameters = withContext< return parameters.reduce((body, parameter) => { const { schema, description } = buildSchemaForParameter.call(this, parameter); - (body.contents || []).forEach(content => { + for (const content of Array.isArray(body.contents) ? body.contents : []) { delete schema.$schema; + delete schema['x-stoplight-id']; - if (description) { + if (typeof description === 'string') { schema.description = description; } @@ -173,13 +180,17 @@ export const translateFromFormDataParameters = withContext< } if (parameter.collectionFormat) { - content.encodings = content.encodings || []; + if (!Array.isArray(content.encodings)) { + content.encodings = []; + } + const encoding = buildEncoding(parameter); if (encoding) { content.encodings.push(encoding); } } - }); + } + return body; }, finalBody); }); diff --git a/src/oas2/transformers/request.ts b/src/oas2/transformers/request.ts index d75b616e..a3d4a6ed 100644 --- a/src/oas2/transformers/request.ts +++ b/src/oas2/transformers/request.ts @@ -1,8 +1,10 @@ import type { IHttpOperationRequest } from '@stoplight/types'; +import pickBy from 'lodash.pickby'; import { isNonNullable } from '../../guards'; import { OasVersion } from '../../oas'; import { createOasParamsIterator } from '../../oas/accessors'; +import { Oas2ParamBase } from '../../oas/guards'; import { getConsumes } from '../accessors'; import { isBodyParam, isFormDataParam, isHeaderParam, isPathParam, isQueryParam } from '../guards'; import { Oas2TranslateFunction } from '../types'; @@ -13,8 +15,6 @@ import { translateToPathParameter, translateToQueryParameter, } from './params'; -import pickBy = require('lodash.pickby'); -import { Oas2ParamBase } from '../../oas/guards'; const iterateOasParams = createOasParamsIterator(OasVersion.OAS2); diff --git a/src/oas2/transformers/responses.ts b/src/oas2/transformers/responses.ts index c41ed3a6..65135675 100644 --- a/src/oas2/transformers/responses.ts +++ b/src/oas2/transformers/responses.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, IHttpOperationResponse, IMediaTypeContent, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import type { Operation } from 'swagger-schema-official'; import { withContext } from '../../context'; diff --git a/src/oas2/transformers/securities.ts b/src/oas2/transformers/securities.ts index ed674d04..ba2bf25e 100644 --- a/src/oas2/transformers/securities.ts +++ b/src/oas2/transformers/securities.ts @@ -8,7 +8,7 @@ import type { IOauthFlowObjects, Optional, } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import type { ApiKeySecurity, BasicAuthenticationSecurity, diff --git a/src/oas2/transformers/servers.ts b/src/oas2/transformers/servers.ts index af564d5a..c7494076 100644 --- a/src/oas2/transformers/servers.ts +++ b/src/oas2/transformers/servers.ts @@ -1,8 +1,7 @@ import type { IServer, Optional } from '@stoplight/types'; +import pickBy from 'lodash.pickby'; import { withContext } from '../../context'; -import pickBy = require('lodash.pickby'); - import { isNonNullable, isString } from '../../guards'; import { ArrayCallbackParameters } from '../../types'; import { isValidScheme } from '../guards'; diff --git a/src/oas3/__tests__/ids.test.ts b/src/oas3/__tests__/ids.test.ts index 7aa3126b..3c47f11c 100644 --- a/src/oas3/__tests__/ids.test.ts +++ b/src/oas3/__tests__/ids.test.ts @@ -1,11 +1,14 @@ import * as fs from 'fs'; import * as path from 'path'; +import { fileURLToPath } from 'url'; import { transformOas3Operations } from '../operation'; import { transformOas3Service } from '../service'; test('should generate proper ids', async () => { - const document = JSON.parse(await fs.promises.readFile(path.join(__dirname, '../__fixtures__/id.json'), 'utf8')); + const document = JSON.parse( + await fs.promises.readFile(path.join(fileURLToPath(import.meta.url), '../../__fixtures__/id.json'), 'utf8'), + ); const { default: output } = await import('../__fixtures__/output'); expect([transformOas3Service({ document }), ...transformOas3Operations(document)]).toEqual(output); diff --git a/src/oas3/accessors.ts b/src/oas3/accessors.ts index 79ea47bb..d1e8abb2 100644 --- a/src/oas3/accessors.ts +++ b/src/oas3/accessors.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import type { DeepPartial, Dictionary, HttpSecurityScheme, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import type { OpenAPIObject } from 'openapi3-ts'; import { isNonNullable } from '../guards'; diff --git a/src/oas3/operation.ts b/src/oas3/operation.ts index ca08e7ca..63deba8b 100644 --- a/src/oas3/operation.ts +++ b/src/oas3/operation.ts @@ -1,5 +1,5 @@ import type { DeepPartial, IHttpOperation } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import type { OpenAPIObject } from 'openapi3-ts'; import { createContext, DEFAULT_ID_GENERATOR } from '../context'; diff --git a/src/oas3/service.ts b/src/oas3/service.ts index 984418ad..c241a84a 100644 --- a/src/oas3/service.ts +++ b/src/oas3/service.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import type { HttpSecurityScheme, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { createContext, DEFAULT_ID_GENERATOR, withContext } from '../context'; import { isNonNullable } from '../guards'; diff --git a/src/oas3/transformers/__tests__/content.test.ts b/src/oas3/transformers/__tests__/content.test.ts index 47017ebd..f9e52919 100644 --- a/src/oas3/transformers/__tests__/content.test.ts +++ b/src/oas3/transformers/__tests__/content.test.ts @@ -14,10 +14,6 @@ const translateHeaderObject = (object: unknown, key: string) => _translateHeaderObject.call(createContext({}, resolveRef, DEFAULT_ID_GENERATOR), [key, object], 0, []); describe('translateMediaTypeObject', () => { - afterEach(() => { - jest.restoreAllMocks(); - }); - it('should gracefully handle invalid data', () => { expect(translateMediaTypeObject({}, null, 'mediaType')).toBeUndefined(); }); diff --git a/src/oas3/transformers/content.ts b/src/oas3/transformers/content.ts index da16f366..e5dce8c7 100644 --- a/src/oas3/transformers/content.ts +++ b/src/oas3/transformers/content.ts @@ -8,7 +8,7 @@ import { Optional, } from '@stoplight/types'; import type { JSONSchema7 } from 'json-schema'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; diff --git a/src/oas3/transformers/examples.ts b/src/oas3/transformers/examples.ts index 5bfb8032..db20fe25 100644 --- a/src/oas3/transformers/examples.ts +++ b/src/oas3/transformers/examples.ts @@ -1,6 +1,6 @@ import { isPlainObject } from '@stoplight/json'; import { INodeExample, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { withContext } from '../../context'; import { isString } from '../../guards'; diff --git a/src/oas3/transformers/request.ts b/src/oas3/transformers/request.ts index 11533957..cfa40fee 100644 --- a/src/oas3/transformers/request.ts +++ b/src/oas3/transformers/request.ts @@ -8,8 +8,8 @@ import type { } from '@stoplight/types'; import { HttpParamStyles } from '@stoplight/types'; import type { JSONSchema7 } from 'json-schema'; +import pickBy from 'lodash.pickby'; import type { ParameterObject } from 'openapi3-ts'; -import pickBy = require('lodash.pickby'); import { withContext } from '../../context'; import { isBoolean, isNonNullable, isString } from '../../guards'; diff --git a/src/oas3/transformers/responses.ts b/src/oas3/transformers/responses.ts index a569b283..54824df1 100644 --- a/src/oas3/transformers/responses.ts +++ b/src/oas3/transformers/responses.ts @@ -1,5 +1,5 @@ import type { IHttpOperationResponse, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; diff --git a/src/oas3/transformers/servers.ts b/src/oas3/transformers/servers.ts index 6e9f5265..9a958635 100644 --- a/src/oas3/transformers/servers.ts +++ b/src/oas3/transformers/servers.ts @@ -1,5 +1,5 @@ import type { INodeVariable, IServer, Optional } from '@stoplight/types'; -import pickBy = require('lodash.pickby'); +import pickBy from 'lodash.pickby'; import { withContext } from '../../context'; import { isNonNullable, isString } from '../../guards'; diff --git a/src/postman/__tests__/util.test.ts b/src/postman/__tests__/util.test.ts index 2b5ae3c6..9d2b8264 100644 --- a/src/postman/__tests__/util.test.ts +++ b/src/postman/__tests__/util.test.ts @@ -1,7 +1,9 @@ -import { Version } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { resolveVersion, transformDescriptionDefinition, transformStringValueToSchema } from '../util'; +const { Version } = postmanCollection; + describe('transformStringValueToSchema()', () => { it('returns param with schema and example', () => { expect(transformStringValueToSchema('test')).toEqual({ diff --git a/src/postman/transformers/__tests__/params.test.ts b/src/postman/transformers/__tests__/params.test.ts index 006de934..9568bf4b 100644 --- a/src/postman/transformers/__tests__/params.test.ts +++ b/src/postman/transformers/__tests__/params.test.ts @@ -1,8 +1,10 @@ import { INodeExample } from '@stoplight/types'; -import { Header, QueryParam, RequestBody } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { transformBody, transformHeader, transformPathParams, transformQueryParam } from '../params'; +const { QueryParam, Header, RequestBody } = postmanCollection; + describe('transformQueryParam()', () => { describe('value is set', () => { it('transforms correctly', () => { diff --git a/src/postman/transformers/__tests__/request.test.ts b/src/postman/transformers/__tests__/request.test.ts index 27b782b9..afcf4c9b 100644 --- a/src/postman/transformers/__tests__/request.test.ts +++ b/src/postman/transformers/__tests__/request.test.ts @@ -1,7 +1,9 @@ -import { Request } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { transformRequest } from '../request'; +const { Request } = postmanCollection; + describe('transformRequest()', () => { it('transforms correctly', () => { expect( diff --git a/src/postman/transformers/__tests__/response.test.ts b/src/postman/transformers/__tests__/response.test.ts index dc502b9a..b2734524 100644 --- a/src/postman/transformers/__tests__/response.test.ts +++ b/src/postman/transformers/__tests__/response.test.ts @@ -1,7 +1,9 @@ -import { Response } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { transformResponse } from '../response'; +const { Response } = postmanCollection; + describe('transformResponse()', () => { describe('media type is defined', () => { it('produces response with content', () => { diff --git a/src/postman/transformers/__tests__/securitySchemes.spec.ts b/src/postman/transformers/__tests__/securitySchemes.spec.ts index 396c4fa4..5168ca9b 100644 --- a/src/postman/transformers/__tests__/securitySchemes.spec.ts +++ b/src/postman/transformers/__tests__/securitySchemes.spec.ts @@ -1,5 +1,6 @@ import { HttpParamStyles } from '@stoplight/types'; -import { Collection, RequestAuth, RequestAuthDefinition } from 'postman-collection'; +import type { RequestAuthDefinition } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { isPostmanSecuritySchemeEqual, @@ -8,6 +9,8 @@ import { transformSecuritySchemes, } from '../securityScheme'; +const { Collection, RequestAuth } = postmanCollection; + describe('transformSecurityScheme()', () => { describe('given basic auth', () => { it('transforms to standard security scheme of type http/basic', () => { diff --git a/src/postman/transformers/__tests__/server.test.ts b/src/postman/transformers/__tests__/server.test.ts index 4f623f42..646122e8 100644 --- a/src/postman/transformers/__tests__/server.test.ts +++ b/src/postman/transformers/__tests__/server.test.ts @@ -1,7 +1,9 @@ -import { Url } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { transformServer } from '../server'; +const { Url } = postmanCollection; + describe('transformServer()', () => { describe('host is defined', () => { describe('port is defined', () => { diff --git a/src/postman/transformers/params.ts b/src/postman/transformers/params.ts index 0bc1b8f5..cf2f168d 100644 --- a/src/postman/transformers/params.ts +++ b/src/postman/transformers/params.ts @@ -8,7 +8,7 @@ import { } from '@stoplight/types'; import type { JSONSchema7 } from 'json-schema'; // @ts-ignore -import * as jsonSchemaGenerator from 'json-schema-generator'; +import jsonSchemaGenerator from 'json-schema-generator'; import type { FormParam, Header, PropertyList, QueryParam, RequestBody } from 'postman-collection'; import * as typeIs from 'type-is'; diff --git a/src/postman/util.ts b/src/postman/util.ts index ca5277f6..e476343c 100644 --- a/src/postman/util.ts +++ b/src/postman/util.ts @@ -1,8 +1,11 @@ import type { IHttpParam } from '@stoplight/types'; -import { Collection, CollectionDefinition, DescriptionDefinition, Version } from 'postman-collection'; +import type { CollectionDefinition, DescriptionDefinition, Version } from 'postman-collection'; +import postmanCollection from 'postman-collection'; import { generateId } from './id'; +const { Collection } = postmanCollection; + export function transformStringValueToSchema(value: string): Pick { return { examples: [ @@ -20,7 +23,7 @@ export function transformDescriptionDefinition(description: string | Description return typeof description === 'string' ? description : description.content; } -export function resolveCollection(collectionDefinition: CollectionDefinition): Collection { +export function resolveCollection(collectionDefinition: CollectionDefinition) { const collection = new Collection(collectionDefinition); return new Collection(collection.toObjectResolved({ variables: collection.variables }, [])); } diff --git a/src/utils.ts b/src/utils.ts index 39c3dee5..3c676cfc 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ import { isPlainObject } from '@stoplight/json'; -import isEqualWith = require('lodash.isequalwith'); +import isEqualWith from 'lodash.isequalwith'; export function entries>(o: { [s: string]: T } | ArrayLike): [string, T][]; export function entries(o: T): [string, T][]; diff --git a/tsconfig.build.json b/tsconfig.build.json index 2efb299d..a80a38fc 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -11,7 +11,6 @@ ], "compilerOptions": { "outDir": "dist", - "moduleResolution": "node", - "target": "ES2019" + "moduleResolution": "node" } } diff --git a/tsconfig.json b/tsconfig.json index c8d501a8..e9405566 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,9 @@ "." ], "compilerOptions": { - "module": "commonjs", - "noUnusedLocals": false + "module": "ESNext", + "noUnusedLocals": false, + "target": "ES2020", + "esModuleInterop": true } } diff --git a/yarn.lock b/yarn.lock index e8658755..9763ad6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,26 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3": +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: "@babel/highlight" "^7.16.7" -"@babel/core@^7.1.0", "@babel/core@^7.7.5": +"@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== + +"@babel/core@^7.1.0": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b" integrity sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA== @@ -30,6 +42,36 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" + integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.9" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/generator@^7.17.9", "@babel/generator@^7.7.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.8.6", "@babel/generator@^7.8.7": version "7.8.8" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.8.tgz#cdcd58caab730834cee9eeadb729e833b625da3e" @@ -40,6 +82,31 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/helper-compilation-targets@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" + integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + "@babel/helper-function-name@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" @@ -56,11 +123,58 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.1", "@babel/helper-plugin-utils@^7.8.0": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + +"@babel/helper-simple-access@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" + integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== + dependencies: + "@babel/types" "^7.17.0" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -73,6 +187,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + +"@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + "@babel/helpers@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" @@ -96,6 +224,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== +"@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" + integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -117,6 +250,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.1" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -166,6 +306,29 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/template@^7.3.3", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" @@ -175,7 +338,23 @@ "@babel/parser" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": +"@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.9" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== @@ -199,19 +378,19 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.16.7", "@babel/types@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cnakazawa/watch@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" - integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - "@commitlint/cli@8.3.5": version "8.3.5" resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-8.3.5.tgz#6d93a3a8b2437fa978999d3f6a336bcc70be3fd3" @@ -357,6 +536,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@faker-js/faker@6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-6.0.0.tgz#b613ebf5f5ebb2ab987afb567d8b7fe860199c13" + integrity sha512-10zLCKhp3YEmBuko71ivcMoIZcCLXgQVck6aNswX+AWwaek/L8S3yz9i8m3tHigRkcF6F2vI+qtdtyySHK+bGA== + "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" @@ -395,158 +579,160 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.1.0.tgz#f67c89e4f4d04dbcf7b052aed5ab9c74f915b954" - integrity sha512-+0lpTHMd/8pJp+Nd4lyip+/Iyf2dZJvcCqrlkeZQoQid+JlThA4M9vxHtheyrQ99jJTMQam+es4BcvZ5W5cC3A== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/types" "^26.1.0" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.1.0" - jest-util "^26.1.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" -"@jest/core@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.1.0.tgz#4580555b522de412a7998b3938c851e4f9da1c18" - integrity sha512-zyizYmDJOOVke4OO/De//aiv8b07OwZzL2cfsvWF3q9YssfpcKfcnZAwDY8f+A76xXSMMYe8i/f/LPocLlByfw== +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== dependencies: - "@jest/console" "^26.1.0" - "@jest/reporters" "^26.1.0" - "@jest/test-result" "^26.1.0" - "@jest/transform" "^26.1.0" - "@jest/types" "^26.1.0" + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" + emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^26.1.0" - jest-config "^26.1.0" - jest-haste-map "^26.1.0" - jest-message-util "^26.1.0" - jest-regex-util "^26.0.0" - jest-resolve "^26.1.0" - jest-resolve-dependencies "^26.1.0" - jest-runner "^26.1.0" - jest-runtime "^26.1.0" - jest-snapshot "^26.1.0" - jest-util "^26.1.0" - jest-validate "^26.1.0" - jest-watcher "^26.1.0" - micromatch "^4.0.2" - p-each-series "^2.1.0" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.1.0.tgz#378853bcdd1c2443b4555ab908cfbabb851e96da" - integrity sha512-86+DNcGongbX7ai/KE/S3/NcUVZfrwvFzOOWX/W+OOTvTds7j07LtC+MgGydH5c8Ri3uIrvdmVgd1xFD5zt/xA== +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== dependencies: - "@jest/fake-timers" "^26.1.0" - "@jest/types" "^26.1.0" - jest-mock "^26.1.0" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" -"@jest/fake-timers@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.1.0.tgz#9a76b7a94c351cdbc0ad53e5a748789f819a65fe" - integrity sha512-Y5F3kBVWxhau3TJ825iuWy++BAuQzK/xEa+wD9vDH3RytW9f2DbMVodfUQC54rZDX3POqdxCgcKdgcOL0rYUpA== +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: - "@jest/types" "^26.1.0" - "@sinonjs/fake-timers" "^6.0.1" - jest-message-util "^26.1.0" - jest-mock "^26.1.0" - jest-util "^26.1.0" + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" -"@jest/globals@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.1.0.tgz#6cc5d7cbb79b76b120f2403d7d755693cf063ab1" - integrity sha512-MKiHPNaT+ZoG85oMaYUmGHEqu98y3WO2yeIDJrs2sJqHhYOy3Z6F7F/luzFomRQ8SQ1wEkmahFAz2291Iv8EAw== +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== dependencies: - "@jest/environment" "^26.1.0" - "@jest/types" "^26.1.0" - expect "^26.1.0" + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" -"@jest/reporters@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.1.0.tgz#08952e90c90282e14ff49e927bdf1873617dae78" - integrity sha512-SVAysur9FOIojJbF4wLP0TybmqwDkdnFxHSPzHMMIYyBtldCW9gG+Q5xWjpMFyErDiwlRuPyMSJSU64A67Pazg== +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.1.0" - "@jest/test-result" "^26.1.0" - "@jest/transform" "^26.1.0" - "@jest/types" "^26.1.0" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" + istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^26.1.0" - jest-resolve "^26.1.0" - jest-util "^26.1.0" - jest-worker "^26.1.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^4.1.3" - optionalDependencies: - node-notifier "^7.0.0" + v8-to-istanbul "^8.1.0" -"@jest/source-map@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.1.0.tgz#a6a020d00e7d9478f4b690167c5e8b77e63adb26" - integrity sha512-XYRPYx4eEVX15cMT9mstnO7hkHP3krNtKfxUYd8L7gbtia8JvZZ6bMzSwa6IQJENbudTwKMw5R1BePRD+bkEmA== +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" source-map "^0.6.0" -"@jest/test-result@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.1.0.tgz#a93fa15b21ad3c7ceb21c2b4c35be2e407d8e971" - integrity sha512-Xz44mhXph93EYMA8aYDz+75mFbarTV/d/x0yMdI3tfSRs/vh4CqSxgzVmCps1fPkHDCtn0tU8IH9iCKgGeGpfw== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/console" "^26.1.0" - "@jest/types" "^26.1.0" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.1.0.tgz#41a6fc8b850c3f33f48288ea9ea517c047e7f14e" - integrity sha512-Z/hcK+rTq56E6sBwMoQhSRDVjqrGtj1y14e2bIgcowARaIE1SgOanwx6gvY4Q9gTKMoZQXbXvptji+q5GYxa6Q== +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== dependencies: - "@jest/test-result" "^26.1.0" - graceful-fs "^4.2.4" - jest-haste-map "^26.1.0" - jest-runner "^26.1.0" - jest-runtime "^26.1.0" + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" -"@jest/transform@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.1.0.tgz#697f48898c2a2787c9b4cb71d09d7e617464e509" - integrity sha512-ICPm6sUXmZJieq45ix28k0s+d/z2E8CHDsq+WwtWI6kW8m7I8kPqarSEcUN86entHQ570ZBRci5OWaKL0wlAWw== +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^26.1.0" - babel-plugin-istanbul "^6.0.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^26.1.0" - jest-regex-util "^26.0.0" - jest-util "^26.1.0" - micromatch "^4.0.2" - pirates "^4.0.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" @@ -561,16 +747,35 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@jest/types@^26.1.0": - version "26.1.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.1.0.tgz#f8afaaaeeb23b5cad49dd1f7779689941dcb6057" - integrity sha512-GXigDDsp6ZlNMhXQDeuy/iYCDsRIHJabWtDzvnn36+aqFfG14JmFV0e/iXxY4SP9vbXSiPNOWdehU5MeqrYHBQ== +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@marionebl/sander@^0.6.0": version "0.6.1" resolved "https://registry.yarnpkg.com/@marionebl/sander/-/sander-0.6.1.tgz#1958965874f24bc51be48875feb50d642fc41f7b" @@ -833,9 +1038,9 @@ resolve "^1.19.0" "@rollup/pluginutils@4.x", "@rollup/pluginutils@^4.1.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08" - integrity sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA== + version "4.1.2" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.2.tgz#ed5821c15e5e05e32816f5fb9ec607cdf5a75751" + integrity sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ== dependencies: estree-walker "^2.0.1" picomatch "^2.2.2" @@ -1009,10 +1214,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -1089,10 +1294,10 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" -"@stoplight/types@^13.0.0-beta.5": - version "13.0.0-beta.5" - resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0-beta.5.tgz#31804f3d1e6369a6daeff985dd0ca7ab27637f70" - integrity sha512-qZgWB3QxoOJL2TsbSfPSyStsnH7BwvAljcT2xa0ZowP3/Eh16OYS/PWzQzTU/GZ+btyqpowGgTragLUgipQAjQ== +"@stoplight/types@^13.0.0": + version "13.0.0" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.0.0.tgz#69a22d57d3a67f623a01929b10e25401c9635154" + integrity sha512-9OTVMiSUz2NlEW14OL6NKOuMTj3dtVVsugRwe3qbq0QnUpx/VLxOuO83n47rXZUTHvk69arOlFrDmRyZMw2DUg== dependencies: "@types/json-schema" "^7.0.4" utility-types "^3.10.0" @@ -1102,7 +1307,7 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" integrity sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": +"@types/babel__core@^7.0.0": version "7.1.9" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== @@ -1113,6 +1318,17 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" +"@types/babel__core@^7.1.14": + version "7.1.19" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" + integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__generator@*": version "7.6.0" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.0.tgz#f1ec1c104d1bb463556ecb724018ab788d0c172a" @@ -1135,6 +1351,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/babel__traverse@^7.0.4": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== + dependencies: + "@babel/types" "^7.3.0" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -1177,6 +1400,13 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + "@types/jest@26.0.3": version "26.0.3" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.3.tgz#79534e0e94857171c0edc596db0ebe7cb7863251" @@ -1248,10 +1478,10 @@ dependencies: "@types/node" "*" -"@types/prettier@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.1.tgz#b6e98083f13faa1e5231bfa3bdb1b0feff536b6d" - integrity sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ== +"@types/prettier@^2.1.5": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== "@types/resolve@1.17.1": version "1.17.1" @@ -1265,10 +1495,10 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/swagger-schema-official@~2.0.21": version "2.0.21" @@ -1294,6 +1524,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^16.0.0": + version "16.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@^5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz#c28ef7f2e688066db0b6a9d95fb74185c114fb9a" @@ -1387,6 +1624,11 @@ abab@^2.0.3: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== +abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + abbrev@1, abbrev@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -1411,11 +1653,11 @@ acorn-walk@^7.1.1: integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== acorn@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" - integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.5.0, acorn@^8.7.0: +acorn@^8.2.4, acorn@^8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== @@ -1525,6 +1767,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -1540,14 +1787,6 @@ any-observable@^0.3.0: resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" @@ -1717,35 +1956,35 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== -babel-jest@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.1.0.tgz#b20751185fc7569a0f135730584044d1cb934328" - integrity sha512-Nkqgtfe7j6PxLO6TnCQQlkMm8wdTdnIF8xrdpooHCuD5hXRzVEPbPneTJKknH5Dsv3L8ip9unHDAp48YQ54Dkg== +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== dependencies: - "@jest/transform" "^26.1.0" - "@jest/types" "^26.1.0" - "@types/babel__core" "^7.1.7" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.1.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-istanbul@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" - integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@istanbuljs/load-nyc-config" "^1.0.0" "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^4.0.0" + istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.1.0.tgz#c6a774da08247a28285620a64dfadbd05dd5233a" - integrity sha512-qhqLVkkSlqmC83bdMhM8WW4Z9tB+JkjqAqlbbohS9sJLT5Ha2vfzuKqg5yenXrAjOPG2YC0WiXdH3a9PvB+YYw== +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -1761,14 +2000,15 @@ babel-polyfill@6.26.0: core-js "^2.5.0" regenerator-runtime "^0.10.5" -babel-preset-current-node-syntax@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" - integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -1776,14 +2016,15 @@ babel-preset-current-node-syntax@^0.1.2: "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.1.0.tgz#612f714e5b457394acfd863793c564cbcdb7d1c1" - integrity sha512-na9qCqFksknlEj5iSdw1ehMVR06LCCTkZLGKeEtxDDdhg8xpUF09m29Kvh1pRbZ07h7AQ5ttLYUwpXL4tO6w7w== +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: - babel-plugin-jest-hoist "^26.1.0" - babel-preset-current-node-syntax "^0.1.2" + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" @@ -1894,6 +2135,17 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browserslist@^4.17.5: + version "4.20.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88" + integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== + dependencies: + caniuse-lite "^1.0.30001317" + electron-to-chromium "^1.4.84" + escalade "^3.1.1" + node-releases "^2.0.2" + picocolors "^1.0.0" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -1913,7 +2165,7 @@ btoa-lite@^1.0.0: resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= -buffer-from@1.x, buffer-from@^1.0.0: +buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -2035,17 +2287,15 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" - integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" +caniuse-lite@^1.0.30001317: + version "1.0.30001325" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz#2b4ad19b77aa36f61f2eaf72e636d7481d55e606" + integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ== capture-stack-trace@^1.0.0: version "1.0.1" @@ -2131,6 +2381,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + cidr-regex@^2.0.10: version "2.0.10" resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-2.0.10.tgz#af13878bd4ad704de77d6dc800799358b3afa70d" @@ -2138,6 +2393,11 @@ cidr-regex@^2.0.10: dependencies: ip-regex "^2.1.0" +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -2282,6 +2542,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -2360,7 +2629,7 @@ columnify@~1.5.4: strip-ansi "^3.0.0" wcwidth "^1.0.0" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -2615,7 +2884,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2644,7 +2913,7 @@ cssom@~0.3.6: resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.2.0: +cssstyle@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== @@ -2771,10 +3040,10 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decimal.js@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" - integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== +decimal.js@^10.2.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decode-uri-component@^0.2.0: version "0.2.0" @@ -2890,10 +3159,10 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" - integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== diff@^4.0.1: version "4.0.1" @@ -2982,11 +3251,21 @@ editor@~1.0.0: resolved "https://registry.yarnpkg.com/editor/-/editor-1.0.0.tgz#60c7f87bd62bcc6a894fa8ccd6afb7823a24f742" integrity sha1-YMf4e9YrzGqJT6jM1q+3gjok90I= +electron-to-chromium@^1.4.84: + version "1.4.105" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.105.tgz#30de5e4ba020140b698539b2d366cd9c3a337ec7" + integrity sha512-6w2bmoQBSUgCQjbSjiVv9IS1lXwW2aQABlUJ1vlE8Vci/sVXxUNQrHLQa5N1ioc82Py+a36DlUA5KvrAlHMMeA== + elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -3090,6 +3369,11 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -3105,13 +3389,13 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^1.14.1: - version "1.14.2" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.2.tgz#14ab71bf5026c2aa08173afba22c6f3173284a84" - integrity sha512-InuOIiKk8wwuOFg6x9BQXbzjrQhtyXh46K9bqVTPzSo2FnyMBaYGBMC6PhQy7yxxil9vIedFBweQBMK74/7o8A== +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== dependencies: esprima "^4.0.1" - estraverse "^4.2.0" + estraverse "^5.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: @@ -3308,7 +3592,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -3333,11 +3617,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -exec-sh@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" - integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== - execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -3395,6 +3674,21 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -3420,17 +3714,15 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.1.0.tgz#8c62e31d0f8d5a8ebb186ee81473d15dd2fbf7c8" - integrity sha512-QbH4LZXDsno9AACrN9eM0zfnby9G+OsdNgZUohjg/P0mLy1O+/bzTAJGT6VSIjVCe8yKM6SzEl/ckEOFBT7Vnw== +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: - "@jest/types" "^26.1.0" - ansi-styles "^4.0.0" - jest-get-type "^26.0.0" - jest-matcher-utils "^26.1.0" - jest-message-util "^26.1.0" - jest-regex-util "^26.0.0" + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" extend-shallow@^2.0.1: version "2.0.1" @@ -3490,11 +3782,6 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -faker@5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" - integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3698,6 +3985,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -3779,7 +4075,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3818,6 +4114,11 @@ gensync@^1.0.0-beta.1: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + gentle-fs@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/gentle-fs/-/gentle-fs-2.3.1.tgz#11201bf66c18f930ddca72cf69460bdfa05727b1" @@ -3840,7 +4141,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -3883,6 +4184,11 @@ get-stream@^5.0.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -4037,10 +4343,10 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== handlebars@^4.4.0: version "4.7.7" @@ -4198,7 +4504,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-agent@^4.0.0: +http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -4242,6 +4548,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -4525,13 +4836,6 @@ is-ci@^1.0.10: dependencies: ci-info "^1.5.0" -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - is-cidr@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-3.1.1.tgz#e92ef121bdec2782271a77ce487a8b8df3718ab7" @@ -4714,10 +5018,10 @@ is-plain-object@^3.0.0: dependencies: isobject "^4.0.0" -is-potential-custom-element-name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" - integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== is-promise@^2.1.0: version "2.1.0" @@ -4817,11 +5121,6 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is-wsl@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" - integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -4875,14 +5174,20 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== +istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== dependencies: - "@babel/core" "^7.7.5" + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" + istanbul-lib-coverage "^3.2.0" semver "^6.3.0" istanbul-lib-report@^3.0.0: @@ -4903,10 +5208,10 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== +istanbul-reports@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -4916,57 +5221,87 @@ java-properties@^1.0.0: resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" integrity sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ== -jest-changed-files@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.1.0.tgz#de66b0f30453bca2aff98e9400f75905da495305" - integrity sha512-HS5MIJp3B8t0NRKGMCZkcDUZo36mVRvrDETl81aqljT1S9tqiHRSpyoOvWg9ZilzZG9TDisDNaN1IXm54fLRZw== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^26.1.0" - execa "^4.0.0" - throat "^5.0.0" + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" -jest-cli@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.1.0.tgz#eb9ec8a18cf3b6aa556d9deaa9e24be12b43ad87" - integrity sha512-Imumvjgi3rU7stq6SJ1JUEMaV5aAgJYXIs0jPqdUnF47N/Tk83EXfmtvNKQ+SnFVI6t6mDOvfM3aA9Sg6kQPSw== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/core" "^26.1.0" - "@jest/test-result" "^26.1.0" - "@jest/types" "^26.1.0" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.1.0" - jest-util "^26.1.0" - jest-validate "^26.1.0" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" - yargs "^15.3.1" + yargs "^16.2.0" -jest-config@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.1.0.tgz#9074f7539acc185e0113ad6d22ed589c16a37a73" - integrity sha512-ONTGeoMbAwGCdq4WuKkMcdMoyfs5CLzHEkzFOlVvcDXufZSaIWh/OXMLa2fwKXiOaFcqEw8qFr4VOKJQfn4CVw== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.1.0" - "@jest/types" "^26.1.0" - babel-jest "^26.1.0" + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" chalk "^4.0.0" + ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" - graceful-fs "^4.2.4" - jest-environment-jsdom "^26.1.0" - jest-environment-node "^26.1.0" - jest-get-type "^26.0.0" - jest-jasmine2 "^26.1.0" - jest-regex-util "^26.0.0" - jest-resolve "^26.1.0" - jest-util "^26.1.0" - jest-validate "^26.1.0" - micromatch "^4.0.2" - pretty-format "^26.1.0" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" jest-diff@^25.2.1: version "25.5.0" @@ -4978,303 +5313,317 @@ jest-diff@^25.2.1: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.1.0.tgz#00a549bdc936c9691eb4dc25d1fbd78bf456abb2" - integrity sha512-GZpIcom339y0OXznsEKjtkfKxNdg7bVbEofK8Q6MnevTIiR1jNhDWKhRX6X0SDXJlwn3dy59nZ1z55fLkAqPWg== +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== dependencies: chalk "^4.0.0" - diff-sequences "^26.0.0" - jest-get-type "^26.0.0" - pretty-format "^26.1.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: detect-newline "^3.0.0" -jest-each@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.1.0.tgz#e35449875009a22d74d1bda183b306db20f286f7" - integrity sha512-lYiSo4Igr81q6QRsVQq9LIkJW0hZcKxkIkHzNeTMPENYYDw/W/Raq28iJ0sLlNFYz2qxxeLnc5K2gQoFYlu2bA== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/types" "^26.1.0" + "@jest/types" "^27.5.1" chalk "^4.0.0" - jest-get-type "^26.0.0" - jest-util "^26.1.0" - pretty-format "^26.1.0" - -jest-environment-jsdom@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.1.0.tgz#9dc7313ffe1b59761dad1fedb76e2503e5d37c5b" - integrity sha512-dWfiJ+spunVAwzXbdVqPH1LbuJW/kDL+FyqgA5YzquisHqTi0g9hquKif9xKm7c1bKBj6wbmJuDkeMCnxZEpUw== - dependencies: - "@jest/environment" "^26.1.0" - "@jest/fake-timers" "^26.1.0" - "@jest/types" "^26.1.0" - jest-mock "^26.1.0" - jest-util "^26.1.0" - jsdom "^16.2.2" - -jest-environment-node@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.1.0.tgz#8bb387b3eefb132eab7826f9a808e4e05618960b" - integrity sha512-DNm5x1aQH0iRAe9UYAkZenuzuJ69VKzDCAYISFHQ5i9e+2Tbeu2ONGY7YStubCLH8a1wdKBgqScYw85+ySxqxg== - dependencies: - "@jest/environment" "^26.1.0" - "@jest/fake-timers" "^26.1.0" - "@jest/types" "^26.1.0" - jest-mock "^26.1.0" - jest-util "^26.1.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" + +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" jest-get-type@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.0.0.tgz#381e986a718998dbfafcd5ec05934be538db4039" - integrity sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== -jest-haste-map@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.1.0.tgz#ef31209be73f09b0d9445e7d213e1b53d0d1476a" - integrity sha512-WeBS54xCIz9twzkEdm6+vJBXgRBQfdbbXD0dk8lJh7gLihopABlJmIQFdWSDDtuDe4PRiObsjZSUjbJ1uhWEpA== +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@jest/types" "^26.1.0" + "@jest/types" "^27.5.1" "@types/graceful-fs" "^4.1.2" + "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-serializer "^26.1.0" - jest-util "^26.1.0" - jest-worker "^26.1.0" - micromatch "^4.0.2" - sane "^4.0.3" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" walker "^1.0.7" - which "^2.0.2" optionalDependencies: - fsevents "^2.1.2" - -jest-jasmine2@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.1.0.tgz#4dfe349b2b2d3c6b3a27c024fd4cb57ac0ed4b6f" - integrity sha512-1IPtoDKOAG+MeBrKvvuxxGPJb35MTTRSDglNdWWCndCB3TIVzbLThRBkwH9P081vXLgiJHZY8Bz3yzFS803xqQ== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.1.0" - "@jest/source-map" "^26.1.0" - "@jest/test-result" "^26.1.0" - "@jest/types" "^26.1.0" + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.1.0" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^26.1.0" - jest-matcher-utils "^26.1.0" - jest-message-util "^26.1.0" - jest-runtime "^26.1.0" - jest-snapshot "^26.1.0" - jest-util "^26.1.0" - pretty-format "^26.1.0" - throat "^5.0.0" - -jest-leak-detector@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.1.0.tgz#039c3a07ebcd8adfa984b6ac015752c35792e0a6" - integrity sha512-dsMnKF+4BVOZwvQDlgn3MG+Ns4JuLv8jNvXH56bgqrrboyCbI1rQg6EI5rs+8IYagVcfVP2yZFKfWNZy0rK0Hw== - dependencies: - jest-get-type "^26.0.0" - pretty-format "^26.1.0" - -jest-matcher-utils@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.1.0.tgz#cf75a41bd413dda784f022de5a65a2a5c73a5c92" - integrity sha512-PW9JtItbYvES/xLn5mYxjMd+Rk+/kIt88EfH3N7w9KeOrHWaHrdYPnVHndGbsFGRJ2d5gKtwggCvkqbFDoouQA== + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== + dependencies: + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== dependencies: chalk "^4.0.0" - jest-diff "^26.1.0" - jest-get-type "^26.0.0" - pretty-format "^26.1.0" - -jest-message-util@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.1.0.tgz#52573fbb8f5cea443c4d1747804d7a238a3e233c" - integrity sha512-dY0+UlldiAJwNDJ08SF0HdF32g9PkbF2NRK/+2iMPU40O6q+iSn1lgog/u0UH8ksWoPv0+gNq8cjhYO2MFtT0g== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.1.0" - "@types/stack-utils" "^1.0.1" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" slash "^3.0.0" - stack-utils "^2.0.2" + stack-utils "^2.0.3" -jest-mock@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.1.0.tgz#80d8286da1f05a345fbad1bfd6fa49a899465d3d" - integrity sha512-1Rm8EIJ3ZFA8yCIie92UbxZWj9SuVmUGcyhLHyAhY6WI3NIct38nVcfOPWhJteqSn8V8e3xOMha9Ojfazfpovw== +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== dependencies: - "@jest/types" "^26.1.0" + "@jest/types" "^27.5.1" + "@types/node" "*" -jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.1.0.tgz#1ce36472f864a5dadf7dc82fa158e1c77955691b" - integrity sha512-fQVEPHHQ1JjHRDxzlLU/buuQ9om+hqW6Vo928aa4b4yvq4ZHBtRSDsLdKQLuCqn5CkTVpYZ7ARh2fbA8WkRE6g== +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^26.1.0" - jest-regex-util "^26.0.0" - jest-snapshot "^26.1.0" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" -jest-resolve@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.1.0.tgz#a530eaa302b1f6fa0479079d1561dd69abc00e68" - integrity sha512-KsY1JV9FeVgEmwIISbZZN83RNGJ1CC+XUCikf/ZWJBX/tO4a4NvA21YixokhdR9UnmPKKAC4LafVixJBrwlmfg== +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: - "@jest/types" "^26.1.0" + "@jest/types" "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.1" - jest-util "^26.1.0" - read-pkg-up "^7.0.1" - resolve "^1.17.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.1.0.tgz#457f7fc522afe46ca6db1dccf19f87f500b3288d" - integrity sha512-elvP7y0fVDREnfqit0zAxiXkDRSw6dgCkzPCf1XvIMnSDZ8yogmSKJf192dpOgnUVykmQXwYYJnCx641uLTgcw== +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: - "@jest/console" "^26.1.0" - "@jest/environment" "^26.1.0" - "@jest/test-result" "^26.1.0" - "@jest/types" "^26.1.0" + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-config "^26.1.0" - jest-docblock "^26.0.0" - jest-haste-map "^26.1.0" - jest-jasmine2 "^26.1.0" - jest-leak-detector "^26.1.0" - jest-message-util "^26.1.0" - jest-resolve "^26.1.0" - jest-runtime "^26.1.0" - jest-util "^26.1.0" - jest-worker "^26.1.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" - throat "^5.0.0" - -jest-runtime@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.1.0.tgz#45a37af42115f123ed5c51f126c05502da2469cb" - integrity sha512-1qiYN+EZLmG1QV2wdEBRf+Ci8i3VSfIYLF02U18PiUDrMbhfpN/EAMMkJtT02jgJUoaEOpHAIXG6zS3QRMzRmA== - dependencies: - "@jest/console" "^26.1.0" - "@jest/environment" "^26.1.0" - "@jest/fake-timers" "^26.1.0" - "@jest/globals" "^26.1.0" - "@jest/source-map" "^26.1.0" - "@jest/test-result" "^26.1.0" - "@jest/transform" "^26.1.0" - "@jest/types" "^26.1.0" - "@types/yargs" "^15.0.0" + throat "^6.0.1" + +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" + cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - exit "^0.1.2" + execa "^5.0.0" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-config "^26.1.0" - jest-haste-map "^26.1.0" - jest-message-util "^26.1.0" - jest-mock "^26.1.0" - jest-regex-util "^26.0.0" - jest-resolve "^26.1.0" - jest-snapshot "^26.1.0" - jest-util "^26.1.0" - jest-validate "^26.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.3.1" -jest-serializer@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.1.0.tgz#72a394531fc9b08e173dc7d297440ac610d95022" - integrity sha512-eqZOQG/0+MHmr25b2Z86g7+Kzd5dG9dhCiUoyUNJPgiqi38DqbDEOlHcNijyfZoj74soGBohKBZuJFS18YTJ5w== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: - graceful-fs "^4.2.4" + "@types/node" "*" + graceful-fs "^4.2.9" -jest-snapshot@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.1.0.tgz#c36ed1e0334bd7bd2fe5ad07e93a364ead7e1349" - integrity sha512-YhSbU7eMTVQO/iRbNs8j0mKRxGp4plo7sJ3GzOQ0IYjvsBiwg0T1o0zGQAYepza7lYHuPTrG5J2yDd0CE2YxSw== +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/types" "^26.1.0" - "@types/prettier" "^2.0.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^26.1.0" - graceful-fs "^4.2.4" - jest-diff "^26.1.0" - jest-get-type "^26.0.0" - jest-haste-map "^26.1.0" - jest-matcher-utils "^26.1.0" - jest-message-util "^26.1.0" - jest-resolve "^26.1.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^26.1.0" + pretty-format "^27.5.1" semver "^7.3.2" -jest-util@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.1.0.tgz#80e85d4ba820decacf41a691c2042d5276e5d8d8" - integrity sha512-rNMOwFQevljfNGvbzNQAxdmXQ+NawW/J72dmddsK0E8vgxXCMtwQ/EH0BiWEIxh0hhMcTsxwAxINt7Lh46Uzbg== +jest-util@^27.0.0, jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: - "@jest/types" "^26.1.0" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" -jest-validate@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.1.0.tgz#942c85ad3d60f78250c488a7f85d8f11a29788e7" - integrity sha512-WPApOOnXsiwhZtmkDsxnpye+XLb/tUISP+H6cHjfUIXvlG+eKwP+isnivsxlHCPaO9Q5wvbhloIBkdF3qUn+Nw== +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== dependencies: - "@jest/types" "^26.1.0" - camelcase "^6.0.0" + "@jest/types" "^27.5.1" + camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^26.0.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^26.1.0" + pretty-format "^27.5.1" -jest-watcher@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.1.0.tgz#99812a0cd931f0cb3d153180426135ab83e4d8f2" - integrity sha512-ffEOhJl2EvAIki613oPsSG11usqnGUzIiK7MMX6hE4422aXOcVEG3ySCTDFLn1+LZNXGPE8tuJxhp8OBJ1pgzQ== +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: - "@jest/test-result" "^26.1.0" - "@jest/types" "^26.1.0" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.1.0" + jest-util "^27.5.1" string-length "^4.0.1" -jest-worker@^26.1.0, jest-worker@^26.2.1: +jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -5283,14 +5632,23 @@ jest-worker@^26.1.0, jest-worker@^26.2.1: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.1.0.tgz#2f3aa7bcffb9bfd025473f83bbbf46a3af026263" - integrity sha512-LIti8jppw5BcQvmNJe4w2g1N/3V68HUfAv9zDVm7v+VAtQulGhH0LnmmiVkbNE4M4I43Bj2fXPiBGKt26k9tHw== +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - "@jest/core" "^26.1.0" + "@jest/core" "^27.5.1" import-local "^3.0.2" - jest-cli "^26.1.0" + jest-cli "^27.5.1" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5317,36 +5675,37 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.2.2: - version "16.2.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.2.2.tgz#76f2f7541646beb46a938f5dc476b88705bedf2b" - integrity sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg== +jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== dependencies: - abab "^2.0.3" - acorn "^7.1.1" + abab "^2.0.5" + acorn "^8.2.4" acorn-globals "^6.0.0" cssom "^0.4.4" - cssstyle "^2.2.0" + cssstyle "^2.3.0" data-urls "^2.0.0" - decimal.js "^10.2.0" + decimal.js "^10.2.1" domexception "^2.0.1" - escodegen "^1.14.1" + escodegen "^2.0.0" + form-data "^3.0.0" html-encoding-sniffer "^2.0.1" - is-potential-custom-element-name "^1.0.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" nwsapi "^2.2.0" - parse5 "5.1.1" - request "^2.88.2" - request-promise-native "^1.0.8" - saxes "^5.0.0" + parse5 "6.0.1" + saxes "^5.0.1" symbol-tree "^3.2.4" - tough-cookie "^3.0.1" + tough-cookie "^4.0.0" w3c-hr-time "^1.0.2" w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.0.0" + webidl-conversions "^6.1.0" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - ws "^7.2.3" + whatwg-url "^8.5.0" + ws "^7.4.6" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -5359,6 +5718,11 @@ json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-bet resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-promise@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/json-promise/-/json-promise-1.1.8.tgz#7b74120422d16ddb449aa3170403fc69ad416402" @@ -5411,6 +5775,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + jsonc-parser@~2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.1.tgz#db73cd59d78cce28723199466b2a03d1be1df2bc" @@ -5931,7 +6300,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.1: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6012,11 +6381,11 @@ macos-release@^2.2.0: integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== magic-string@0.25.x, magic-string@^0.25.7: - version "0.25.9" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== dependencies: - sourcemap-codec "^1.4.8" + sourcemap-codec "^1.4.4" make-dir@^1.0.0: version "1.3.0" @@ -6178,15 +6547,7 @@ merge@^1.2.1: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== -micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" - -micromatch@^3.0.4, micromatch@^3.1.4: +micromatch@^3.0.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -6205,11 +6566,24 @@ micromatch@^3.0.4, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + mime-db@1.49.0: version "1.49.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-format@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mime-format/-/mime-format-2.0.1.tgz#1274876d58bc803332427a515f5f7036e07b9413" @@ -6217,7 +6591,14 @@ mime-format@2.0.1: dependencies: charset "^1.0.0" -mime-types@2.1.32, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@2.1.35: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.32" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== @@ -6259,7 +6640,7 @@ minimist@1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -6308,11 +6689,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@1.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -6442,22 +6818,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-notifier@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-7.0.1.tgz#a355e33e6bebacef9bf8562689aed0f4230ca6f9" - integrity sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg== - dependencies: - growly "^1.3.0" - is-wsl "^2.1.1" - semver "^7.2.1" - shellwords "^0.1.1" - uuid "^7.0.3" - which "^2.0.2" +node-releases@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" + integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== nopt@^4.0.1, nopt@^4.0.3: version "4.0.3" @@ -6477,13 +6841,6 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -6602,7 +6959,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -6877,6 +7234,13 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + openapi3-ts@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-2.0.1.tgz#b270aecea09e924f1886bc02a72608fca5a98d85" @@ -7157,15 +7521,25 @@ parse-json@^5.0.0: json-parse-better-errors "^1.0.1" lines-and-columns "^1.1.6" +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= -parse5@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== pascalcase@^0.1.1: version "0.1.1" @@ -7239,6 +7613,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.2, picomatch@^2.2.3: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -7254,12 +7633,10 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= -pirates@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-conf@^2.1.0: version "2.1.0" @@ -7289,18 +7666,18 @@ posix-character-classes@^0.1.0: integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postman-collection@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.0.tgz#c5833aa3cb82df79cc5d16e5d7399c71a84ea4fa" - integrity sha512-J9IpCMXpGDLN7MGhdMcUbZ0SIWLCcTVdrjTgKVYubkW1sn1KcDqJgsdTr/ItkO8dOXKLuhvnq2QnE5Vrzb3WMA== + version "4.1.2" + resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.2.tgz#977f702a68d680743628938259d72e31f3ff476f" + integrity sha512-nRYgzeo2VwQZZXF8YUMUvG8wKXPmjQ+BZPMXix+tNRWBHWAdqa191AwBL80LQxvHzECYp8Lp1JXMpg4OMXHLnw== dependencies: - faker "5.5.3" + "@faker-js/faker" "6.0.0" file-type "3.9.0" http-reasons "0.1.0" iconv-lite "0.6.3" liquid-json "0.3.1" lodash "4.17.21" mime-format "2.0.1" - mime-types "2.1.32" + mime-types "2.1.35" postman-url-encoder "3.0.5" semver "7.3.5" uuid "8.3.2" @@ -7354,15 +7731,14 @@ pretty-format@^25.2.1, pretty-format@^25.5.0: ansi-styles "^4.0.0" react-is "^16.12.0" -pretty-format@^26.1.0: - version "26.1.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.1.0.tgz#272b9cd1f1a924ab5d443dc224899d7a65cb96ec" - integrity sha512-GmeO1PEYdM+non4BKCj+XsPJjFOJIPnsLewqhDVoqY1xo0yNmDas7tC2XwpMrRAHR3MaE2hPo37deX5OisJ2Wg== +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: - "@jest/types" "^26.1.0" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" process-nextick-args@~2.0.0: version "2.0.1" @@ -7428,7 +7804,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== @@ -7519,6 +7895,11 @@ react-is@^16.12.0, react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + read-cmd-shim@^1.0.1, read-cmd-shim@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -7577,7 +7958,7 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg-up@^7.0.0, read-pkg-up@^7.0.1: +read-pkg-up@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== @@ -7739,11 +8120,6 @@ registry-url@^3.0.3: dependencies: rc "^1.0.1" -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" @@ -7754,23 +8130,7 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== - dependencies: - lodash "^4.17.15" - -request-promise-native@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== - dependencies: - request-promise-core "1.1.3" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.81.0, request@^2.88.0, request@^2.88.2: +request@^2.81.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -7853,6 +8213,11 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + resolve@1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" @@ -7955,17 +8320,12 @@ rollup-plugin-typescript2@^0.30.0: tslib "2.1.0" rollup@^2.47.0: - version "2.70.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.1.tgz#824b1f1f879ea396db30b0fc3ae8d2fead93523e" - integrity sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA== + version "2.63.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.63.0.tgz#fe2f7fec2133f3fab9e022b9ac245628d817c6bb" + integrity sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ== optionalDependencies: fsevents "~2.3.2" -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - run-async@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -8017,22 +8377,7 @@ safe-stable-stringify@^1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - -saxes@^5.0.0: +saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== @@ -8107,7 +8452,7 @@ semver@6.3.0, semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@7.3.5, semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.3.2, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -8185,11 +8530,6 @@ shelljs@0.8.x: interpret "^1.0.0" rechoir "^0.6.2" -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -8204,6 +8544,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + signale@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/signale/-/signale-1.4.0.tgz#c4be58302fb0262ac00fc3d886a7c113759042f1" @@ -8336,7 +8681,7 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -sourcemap-codec@^1.4.8: +sourcemap-codec@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== @@ -8432,10 +8777,10 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -stack-utils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" - integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== +stack-utils@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== dependencies: escape-string-regexp "^2.0.0" @@ -8447,11 +8792,6 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - stream-combiner2@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" @@ -8684,6 +9024,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" @@ -8768,11 +9115,10 @@ terminal-link@^2.0.0: supports-hyperlinks "^2.0.0" terser@^5.0.0: - version "5.12.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.12.1.tgz#4cf2ebed1f5bceef5c83b9f60104ac4a78b49e9c" - integrity sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ== + version "5.10.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" + integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== dependencies: - acorn "^8.5.0" commander "^2.20.0" source-map "~0.7.2" source-map-support "~0.5.20" @@ -8796,10 +9142,10 @@ text-table@^0.2.0, text-table@~0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2@^2.0.0, through2@^2.0.2, through2@~2.0.0: version "2.0.5" @@ -8880,20 +9226,20 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@^2.3.3, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== dependencies: - psl "^1.1.28" + psl "^1.1.33" punycode "^2.1.1" + universalify "^0.1.2" -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: - ip-regex "^2.1.0" psl "^1.1.28" punycode "^2.1.1" @@ -8904,6 +9250,13 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" @@ -8924,21 +9277,19 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -ts-jest@26.1.1: - version "26.1.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.1.tgz#b98569b8a4d4025d966b3d40c81986dd1c510f8d" - integrity sha512-Lk/357quLg5jJFyBQLnSbhycnB3FPe+e9i7ahxokyXxAYoB0q1pPmqxxRPYr4smJic1Rjcf7MXDBhZWgxlli0A== +ts-jest@^27.1.4: + version "27.1.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" + integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== dependencies: bs-logger "0.x" - buffer-from "1.x" fast-json-stable-stringify "2.x" + jest-util "^27.0.0" json5 "2.x" lodash.memoize "4.x" make-error "1.x" - micromatch "4.x" - mkdirp "1.x" semver "7.x" - yargs-parser "18.x" + yargs-parser "20.x" ts-node@^8.10.2: version "8.10.2" @@ -9146,7 +9497,7 @@ universal-user-agent@^5.0.0: dependencies: os-name "^3.1.0" -universalify@^0.1.0: +universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== @@ -9246,20 +9597,15 @@ uuid@^3.3.2, uuid@^3.3.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - v8-compile-cache@^2.0.3: version "2.1.1" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== -v8-to-istanbul@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" - integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -9303,7 +9649,7 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= @@ -9322,7 +9668,7 @@ webidl-conversions@^5.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== -webidl-conversions@^6.0.0: +webidl-conversions@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== @@ -9348,6 +9694,15 @@ whatwg-url@^8.0.0: tr46 "^2.0.2" webidl-conversions "^5.0.0" +whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -9376,7 +9731,7 @@ which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: dependencies: isexe "^2.0.0" -which@^2.0.1, which@^2.0.2: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -9460,6 +9815,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -9484,10 +9848,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^7.2.3: - version "7.3.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" - integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== +ws@^7.4.6: + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== xdg-basedir@^3.0.0: version "3.0.0" @@ -9519,6 +9883,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -9539,13 +9908,10 @@ yaml@^1.10.0, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@18.x, yargs-parser@^18.1.1: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^10.0.0: version "10.1.0" @@ -9554,6 +9920,14 @@ yargs-parser@^10.0.0: dependencies: camelcase "^4.1.0" +yargs-parser@^18.1.1: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" @@ -9586,7 +9960,7 @@ yargs@^11.0.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@^15.0.1, yargs@^15.3.1: +yargs@^15.0.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== @@ -9603,6 +9977,19 @@ yargs@^15.0.1, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360"