From ebbc6c1546aced8db0f492dd80651d2459c9bae0 Mon Sep 17 00:00:00 2001 From: "Daniel A. White" Date: Tue, 16 Aug 2022 11:21:33 -0400 Subject: [PATCH] fix: upgrade dependencies and resolve breaking http spec changes (#2105) --- packages/cli/package.json | 4 +- packages/cli/src/extensions.ts | 2 +- packages/cli/src/operations.ts | 2 +- packages/http/src/validator/index.ts | 68 +++++++-------- .../E2E-Recursive-Object-Handling.oas3.txt | 50 +++++++++++ yarn.lock | 87 +++++++++++-------- 6 files changed, 137 insertions(+), 76 deletions(-) create mode 100644 test-harness/specs/references/E2E-Recursive-Object-Handling.oas3.txt diff --git a/packages/cli/package.json b/packages/cli/package.json index 5bce2e789..18c024b5d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -7,13 +7,13 @@ }, "bugs": "https://github.com/stoplightio/prism/issues", "dependencies": { - "@stoplight/http-spec": "^5.1.1", + "@stoplight/http-spec": "^5.5.2", "@stoplight/json": "^3.18.1", "@stoplight/json-schema-ref-parser": "9.2.1", "@stoplight/prism-core": "^4.10.2", "@stoplight/prism-http": "^4.10.2", "@stoplight/prism-http-server": "^4.10.2", - "@stoplight/types": "^13.0.0", + "@stoplight/types": "^13.6.0", "chalk": "^4.1.2", "chokidar": "^3.5.2", "fp-ts": "^2.11.5", diff --git a/packages/cli/src/extensions.ts b/packages/cli/src/extensions.ts index 0a5ddaba1..a8fe51d9a 100644 --- a/packages/cli/src/extensions.ts +++ b/packages/cli/src/extensions.ts @@ -1,4 +1,4 @@ -import { dereference } from 'json-schema-ref-parser'; +import { dereference } from '@stoplight/json-schema-ref-parser'; import { decycle } from '@stoplight/json'; import { get, camelCase, forOwn } from 'lodash'; import * as jsf from 'json-schema-faker'; diff --git a/packages/cli/src/operations.ts b/packages/cli/src/operations.ts index b6de2234f..20784bc78 100644 --- a/packages/cli/src/operations.ts +++ b/packages/cli/src/operations.ts @@ -1,7 +1,7 @@ import { transformOas3Operations } from '@stoplight/http-spec/oas3/operation'; import { transformOas2Operations } from '@stoplight/http-spec/oas2/operation'; import { transformPostmanCollectionOperations } from '@stoplight/http-spec/postman/operation'; -import { dereference } from 'json-schema-ref-parser'; +import { dereference } from '@stoplight/json-schema-ref-parser'; import { bundleTarget, decycle } from '@stoplight/json'; import { IHttpOperation } from '@stoplight/types'; import { get } from 'lodash'; diff --git a/packages/http/src/validator/index.ts b/packages/http/src/validator/index.ts index 09c42539f..bb5aa38b0 100644 --- a/packages/http/src/validator/index.ts +++ b/packages/http/src/validator/index.ts @@ -25,13 +25,14 @@ import { wildcardMediaTypeMatch } from './utils/wildcardMediaTypeMatch'; export { validateSecurity } from './validators/security'; -const checkBodyIsProvided = (requestBody: IHttpOperationRequestBody, body: unknown) => +const checkRequiredBodyIsProvided = (requestBody: O.Option, body: unknown) => pipe( requestBody, - E.fromPredicate>( - requestBody => !(!!requestBody.required && !body), + E.fromPredicate, NonEmptyArray>( + requestBody => O.isNone(requestBody) || !(!!requestBody.value.required && !body), () => [{ code: 'required', message: 'Body parameter is required', severity: DiagnosticSeverity.Error }] - ) + ), + E.map(requestBody => [requestBody, O.fromNullable(body)] as const) ); const isMediaTypeSupportedInContents = (mediaType?: string, contents?: IMediaTypeContent[]): boolean => @@ -51,63 +52,60 @@ const isMediaTypeSupportedInContents = (mediaType?: string, contents?: IMediaTyp ); const validateInputIfBodySpecIsProvided = ( - body: unknown, + body: O.Option, mediaType: string, - contents?: IMediaTypeContent[], + requestBody: O.Option, bundle?: unknown ) => pipe( - sequenceOption(O.fromNullable(body), O.fromNullable(contents)), + sequenceOption(body, requestBody), O.fold( () => E.right(body), - ([body, contents]) => validateBody(body, contents, ValidationContext.Input, mediaType, bundle) + ([body, contents]) => validateBody(body, contents.contents ?? [], ValidationContext.Input, mediaType, bundle) ) ); -const tryValidateInputBody = ( - requestBody: IHttpOperationRequestBody, +const validateInputBody = ( + requestBody: O.Option, bundle: unknown, body: unknown, headers: IHttpNameValue ) => pipe( - checkBodyIsProvided(requestBody, body), - E.chain(() => { - const headersNormalized = caseless(headers || {}); - - const contentLength = parseInt(headersNormalized.get('content-length')) || 0; + checkRequiredBodyIsProvided(requestBody, body), + E.map(b => [...b, caseless(headers || {})] as const), + E.chain(([requestBody, body, headers]) => { + const contentLength = parseInt(headers.get('content-length')) || 0; if (contentLength === 0) { // generously allow this content type if there isn't a body actually provided - return E.right(body); + return E.right([requestBody, body, headers] as const); } - const mediaType = headersNormalized.get('content-type'); - if (isMediaTypeSupportedInContents(mediaType, requestBody.contents)) { - return E.right(body); - } - - const specRequestBodyContents = requestBody.contents || []; - let message: string; - - if (specRequestBodyContents.length === 0) { - message = 'No supported content types, but request included a non-empty body'; - } else { - const supportedContentTypes = specRequestBodyContents.map(x => x.mediaType); - message = `Supported content types: ${supportedContentTypes.join(',')}`; + let errorMessage = 'No supported content types, but request included a non-empty body'; + if (O.isSome(requestBody)) { + const mediaType = headers.get('content-type'); + if (isMediaTypeSupportedInContents(mediaType, requestBody.value.contents)) { + return E.right([requestBody, body, headers] as const); + } + + const specRequestBodyContents = requestBody.value.contents || []; + if (specRequestBodyContents.length > 0) { + const supportedContentTypes = specRequestBodyContents.map(x => x.mediaType); + errorMessage = `Supported content types: ${supportedContentTypes.join(',')}`; + } } return E.left>([ { - message, + message: errorMessage, code: 415, severity: DiagnosticSeverity.Error, }, ]); }), - E.chain(() => { - const mediaType = caseless(headers || {}).get('content-type'); - - return validateInputIfBodySpecIsProvided(body, mediaType, requestBody.contents, bundle); + E.chain(([requestBody, body, headers]) => { + const mediaType = headers.get('content-type'); + return validateInputIfBodySpecIsProvided(body, mediaType, requestBody, bundle); }) ); @@ -122,7 +120,7 @@ export const validateInput: ValidatorFn = ({ resou e => E.right, unknown>(e), request => sequenceValidation( - request.body ? tryValidateInputBody(request.body, bundle, body, element.headers || {}) : E.right(undefined), + validateInputBody(O.fromNullable(request.body), bundle, body, element.headers || {}), request.headers ? validateHeaders(element.headers || {}, request.headers, bundle) : E.right(undefined), request.query ? validateQuery(element.url.query || {}, request.query, bundle) : E.right(undefined), request.path diff --git a/test-harness/specs/references/E2E-Recursive-Object-Handling.oas3.txt b/test-harness/specs/references/E2E-Recursive-Object-Handling.oas3.txt new file mode 100644 index 000000000..da069d0d7 --- /dev/null +++ b/test-harness/specs/references/E2E-Recursive-Object-Handling.oas3.txt @@ -0,0 +1,50 @@ +====test==== +If I have an OAS3 document referencing a model that +recursively references itself in a request body through other objects +it should return 200 on a regular request and not crash +====spec==== +openapi: 3.0.0 +paths: + /: + put: + requestBody: + content: + application/json: + schema: + "$ref": "#/components/schemas/RequestModel" + required: true + responses: + '200': + content: + text/plain: + schema: + type: string + example: + valid request +components: + schemas: + RequestModel: + title: RequestModel + type: object + properties: + data: + anyOf: + - type: array + items: + "$ref": "#/components/schemas/Document" + Document: + title: Document + type: object + properties: + chunks: + type: array + items: + "$ref": "#/components/schemas/Document" +====server==== +mock -p 4010 ${document} +====command==== +curl -i -X PUT http://localhost:4010/ -H 'content-type: application/json' -d '{}' +====expect==== +HTTP/1.1 200 OK + +valid request diff --git a/yarn.lock b/yarn.lock index 07980c940..5fee258c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -625,7 +625,12 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@faker-js/faker@6.0.0", "@faker-js/faker@^6.0.0": +"@faker-js/faker@5.5.3": + version "5.5.3" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-5.5.3.tgz#18e3af6b8eae7984072bbeb0c0858474d7c4cefe" + integrity sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw== + +"@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== @@ -1983,23 +1988,23 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@stoplight/http-spec@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@stoplight/http-spec/-/http-spec-5.1.1.tgz#80e031cf428798d8011983573ee818cd6e1fb3af" - integrity sha512-KKkImg/jNzMAFOPHllfHTlMld7Ipf+wy9u81H2W7x40tpYQEj6cggvX+xpBFThg8N6vevq/bQ5VeH3pr0jlTUw== +"@stoplight/http-spec@^5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@stoplight/http-spec/-/http-spec-5.5.2.tgz#de9d4e282743c9fb09a1ed1f7e293a4b7583f6dd" + integrity sha512-WW2CeTxIuvbT59lm+n47iyxLHO/Uj73Mnta2uW39WdE3uUkJIEqovFJPflsIrJCXUcNIIwLuB+I6ZMTuTGK2Uw== dependencies: - "@stoplight/json" "^3.18.0" + "@stoplight/json" "^3.18.1" "@stoplight/json-schema-generator" "1.0.1" - "@stoplight/types" "^13.0.0" - "@types/json-schema" "7.0.5" - "@types/swagger-schema-official" "~2.0.21" + "@stoplight/types" "^13.6.0" + "@types/json-schema" "7.0.11" + "@types/swagger-schema-official" "~2.0.22" "@types/type-is" "^1.6.3" fnv-plus "^1.3.1" lodash.isequalwith "^4.4.0" lodash.pick "^4.4.0" lodash.pickby "^4.6.0" - openapi3-ts "^2.0.1" - postman-collection "^4.1.0" + openapi3-ts "^2.0.2" + postman-collection "^4.1.2" tslib "^2.3.1" type-is "^1.6.18" @@ -2044,7 +2049,7 @@ "@types/json-schema" "^7.0.7" json-pointer "^0.6.1" -"@stoplight/json@^3.18.0", "@stoplight/json@^3.18.1": +"@stoplight/json@^3.18.1": version "3.18.1" resolved "https://registry.yarnpkg.com/@stoplight/json/-/json-3.18.1.tgz#725b34b24e8b0c0f73113362be54c7a4f294cdba" integrity sha512-QmELAqBS8DC+8YuG7+OvDVP6RaUVi8bzN0KKW2UEcZg+0a1sqeeZgfW079AmJIZg8HEN7udAt4iozIB8Dm0t1Q== @@ -2073,6 +2078,14 @@ "@types/json-schema" "^7.0.4" utility-types "^3.10.0" +"@stoplight/types@^13.6.0": + version "13.6.0" + resolved "https://registry.yarnpkg.com/@stoplight/types/-/types-13.6.0.tgz#96c6aaae05858b36f589821cd52c95aa9b205ce7" + integrity sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ== + dependencies: + "@types/json-schema" "^7.0.4" + utility-types "^3.10.0" + "@stoplight/yaml-ast-parser@0.0.48": version "0.0.48" resolved "https://registry.yarnpkg.com/@stoplight/yaml-ast-parser/-/yaml-ast-parser-0.0.48.tgz#442b21f419427acaa8a3106ebc5d73351c407002" @@ -2196,10 +2209,10 @@ jest-diff "^27.0.0" pretty-format "^27.0.0" -"@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.11": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.7": version "7.0.7" @@ -2326,7 +2339,7 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== -"@types/swagger-schema-official@~2.0.21": +"@types/swagger-schema-official@~2.0.22": version "2.0.22" resolved "https://registry.yarnpkg.com/@types/swagger-schema-official/-/swagger-schema-official-2.0.22.tgz#f7e06168e6994574dfd86928ac04b196870ab043" integrity sha512-7yQiX6MWSFSvc/1wW5smJMZTZ4fHOd+hqLr3qr/HONDxHEa2bnYAsOcGBOEqFIjd4yetwMOdEDdeW+udRAQnHA== @@ -7181,12 +7194,12 @@ ono@^4.0.11: dependencies: format-util "^1.0.3" -openapi3-ts@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-2.0.1.tgz#b270aecea09e924f1886bc02a72608fca5a98d85" - integrity sha512-v6X3iwddhi276siej96jHGIqTx3wzVfMTmpGJEQDt7GPI7pI6sywItURLzpEci21SBRpPN/aOWSF5mVfFVNmcg== +openapi3-ts@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-2.0.2.tgz#a200dd838bf24c9086c8eedcfeb380b7eb31e82a" + integrity sha512-TxhYBMoqx9frXyOgnRHufjQfPXomTIHYKhSKJ6jHfj13kS8OEIhvmE8CTuQyKtjjWttAjX5DPxM1vmalEpo8Qw== dependencies: - yaml "^1.10.0" + yaml "^1.10.2" opencollective-postinstall@^2.0.2: version "2.0.2" @@ -7573,12 +7586,12 @@ postcss@^8.0.2: picocolors "^1.0.0" source-map-js "^1.0.2" -postman-collection@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.2.tgz#977f702a68d680743628938259d72e31f3ff476f" - integrity sha512-nRYgzeo2VwQZZXF8YUMUvG8wKXPmjQ+BZPMXix+tNRWBHWAdqa191AwBL80LQxvHzECYp8Lp1JXMpg4OMXHLnw== +postman-collection@^4.1.2: + version "4.1.5" + resolved "https://registry.yarnpkg.com/postman-collection/-/postman-collection-4.1.5.tgz#7aad6aa9e1694d0bce058cee4ac40427fe541ca8" + integrity sha512-BY3NfP7EYExZG5ER9P82r0ZRc17z88WZAzn121EpWC8FM3HYtFwWJpXOsZk+2MKFn3agCq4JPRhnWw3G6XBXgw== dependencies: - "@faker-js/faker" "6.0.0" + "@faker-js/faker" "5.5.3" file-type "3.9.0" http-reasons "0.1.0" iconv-lite "0.6.3" @@ -7587,7 +7600,7 @@ postman-collection@^4.1.0: mime-format "2.0.1" mime-types "2.1.35" postman-url-encoder "3.0.5" - semver "7.3.5" + semver "7.3.7" uuid "8.3.2" postman-url-encoder@3.0.5: @@ -8098,10 +8111,10 @@ semver-regex@^3.1.2: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.3.5, semver@^7.1.1, semver@^7.1.3, semver@^7.3.4, 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== +semver@7.3.7, semver@^7.0.0, semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" @@ -8117,10 +8130,10 @@ 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.0.0, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.1.1, semver@^7.1.3, semver@^7.3.4, 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" @@ -9337,7 +9350,7 @@ 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.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==