diff --git a/projects/openapi-io/src/write/yaml-roundtrip.ts b/projects/openapi-io/src/write/yaml-roundtrip.ts index a01cf84d9d..bea6e5dec2 100644 --- a/projects/openapi-io/src/write/yaml-roundtrip.ts +++ b/projects/openapi-io/src/write/yaml-roundtrip.ts @@ -67,9 +67,3 @@ function insert(arr: T[], index: number, item: any) { copy.splice(index, 0, item); return copy; } - -function remove(arr: T[], index: number) { - const copy = [...arr]; - copy.splice(index, 1); - return copy; -} diff --git a/projects/optic/src/__tests__/integration/__snapshots__/capture.test.ts.snap b/projects/optic/src/__tests__/integration/__snapshots__/capture.test.ts.snap index 4bcce67d72..a11e955055 100644 --- a/projects/optic/src/__tests__/integration/__snapshots__/capture.test.ts.snap +++ b/projects/optic/src/__tests__/integration/__snapshots__/capture.test.ts.snap @@ -303,7 +303,8 @@ paths: $ref: "#/components/schemas/GetAuthHawk200ResponseBody" /basic-auth: get: - responses: {} + responses: + {} /cookies/{cooky}: parameters: - in: path @@ -312,13 +313,16 @@ paths: schema: type: string get: - responses: {} + responses: + {} /cookies: get: - responses: {} + responses: + {} /deflate: get: - responses: {} + responses: + {} /delay/{delay}: parameters: - in: path @@ -336,7 +340,8 @@ paths: $ref: "#/components/schemas/GetDelayDelay200ResponseBody" /delete: delete: - responses: {} + responses: + {} requestBody: content: text/plain: @@ -344,10 +349,12 @@ paths: type: string /digest-auth: get: - responses: {} + responses: + {} /encoding/utf8: get: - responses: {} + responses: + {} /: get: responses: @@ -359,7 +366,8 @@ paths: $ref: "#/components/schemas/Get200ResponseBody" /get/hello: get: - responses: {} + responses: + {} /get: get: responses: @@ -371,7 +379,8 @@ paths: $ref: "#/components/schemas/GetGet200ResponseBody" /gzip: get: - responses: {} + responses: + {} /headers: get: responses: @@ -383,25 +392,27 @@ paths: $ref: "#/components/schemas/GetHeaders200ResponseBody" /ip: get: - responses: {} + responses: + {} /oauth1: get: responses: - "200": - description: 200 response - content: - application/json: - schema: - $ref: "#/components/schemas/GetAuthHawk200ResponseBody" "401": description: 401 response content: application/json: schema: $ref: "#/components/schemas/GetOauth1401ResponseBody" + "200": + description: 200 response + content: + application/json: + schema: + $ref: "#/components/schemas/GetAuthHawk200ResponseBody" /patch: patch: - responses: {} + responses: + {} requestBody: content: text/plain: @@ -409,7 +420,8 @@ paths: type: string /post: post: - responses: {} + responses: + {} requestBody: content: text/plain: @@ -420,7 +432,8 @@ paths: type: string /put: put: - responses: {} + responses: + {} requestBody: content: text/plain: @@ -461,7 +474,8 @@ paths: schema: type: string get: - responses: {} + responses: + {} /time/{time}: parameters: - in: path @@ -722,7 +736,8 @@ components: - mixed authenticated: type: boolean - required: [] + required: + [] - type: array items: type: object @@ -772,7 +787,8 @@ components: type: string folders: type: array - items: {} + items: + {} requests: type: array items: @@ -790,7 +806,8 @@ components: type: string data: type: array - items: {} + items: + {} rawModeData: type: string tests: @@ -895,7 +912,8 @@ components: properties: variables: type: array - items: {} + items: + {} info: type: object properties: @@ -950,7 +968,8 @@ components: type: string header: type: array - items: {} + items: + {} body: type: object properties: @@ -968,7 +987,8 @@ components: - body response: type: array - items: {} + items: + {} required: - name - request @@ -985,7 +1005,8 @@ components: type: string folders: type: array - items: {} + items: + {} requests: type: array items: @@ -1003,7 +1024,8 @@ components: type: string data: type: array - items: {} + items: + {} rawModeData: type: string tests: @@ -1566,7 +1588,8 @@ GET /books [200 response body] 'author_id' is now type number (/properties/books/items/properties/author_id) [200 response body] 'status' now has enum value 'hold' (/properties/books/items/properties/status/enum) [200 response body] schema (/properties/books/items/properties/price/maximum) with keyword 'maximum' and parameters {"comparison":"<=","limit":6} received invalid values 10, 15 -  ⛔️ schema could not be automatically updated. Update the schema manually at openapi.yml:37:799 +  ⛔️ schema could not be automatically updated. Update the schema manually at openapi.yml:42:924 +...and 1 endpoint that did not receive traffic Learning path patterns for unmatched requests... Documenting new operations: @@ -1585,13 +1608,18 @@ info: version: 0.1.0 paths: /books: + post: + "responses": + {} get: + # a comment about something responses: "200": description: 200 response content: application/json: schema: + # use the refs here $ref: "#/components/schemas/GetBooks200ResponseBody" /authors: get: @@ -1641,6 +1669,7 @@ paths: type: string components: schemas: + # this is a schema GetBooks200ResponseBody: type: object properties: @@ -1751,7 +1780,8 @@ GET /books [200 response body] 'author_id' is now type number (/properties/books/items/properties/author_id) [200 response body] 'status' now has enum value 'hold' (/properties/books/items/properties/status/enum) [200 response body] schema (/properties/books/items/properties/price/maximum) with keyword 'maximum' and parameters {"comparison":"<=","limit":6} received invalid values 10, 15 -  ⛔️ schema could not be automatically updated. Update the schema manually at openapi.yml:37:799 +  ⛔️ schema could not be automatically updated. Update the schema manually at openapi.yml:42:924 +...and 1 endpoint that did not receive traffic 5 unmatched requests New endpoints are only added in interactive mode. Run 'optic capture openapi.yml --update interactive' to add new endpoints @@ -1766,16 +1796,22 @@ info: version: 0.1.0 paths: /books: + post: + "responses": + {} get: + # a comment about something responses: "200": description: 200 response content: application/json: schema: + # use the refs here $ref: "#/components/schemas/GetBooks200ResponseBody" components: schemas: + # this is a schema GetBooks200ResponseBody: type: object properties: @@ -1848,39 +1884,40 @@ GET /books [200 response body] 'updated_at' is not documented (/properties/books/items/properties) [200 response body] 'author_id' does not match type number. Received 6nTxAFM5ck4Hob77hGQoL (/properties/books/items/properties/author_id)  Diff  'author_id' did not match schema -25 | properties: -26 | id: -27 | type: string -28 |  author_id: [Actual] "6nTxAFM5ck4Hob77hGQoL" -29 | type: number -30 | status: -31 | type: string +30 | properties: +31 | id: +32 | type: string +33 |  author_id: [Actual] "6nTxAFM5ck4Hob77hGQoL" +34 | type: number +35 | status: +36 | type: string $workspace$/openapi.yml [200 response body] 'status' missing enum value 'hold' (/properties/books/items/properties/status/enum)  Diff  'status' does not have enum value hold -29 | type: number -30 | status: -31 | type: string -32 |  enum: missing enum value 'hold' -33 | - ready -34 | - not_ready -35 | price: +34 | type: number +35 | status: +36 | type: string +37 |  enum: missing enum value 'hold' +38 | - ready +39 | - not_ready +40 | price: $workspace$/openapi.yml [200 response body] schema (/properties/books/items/properties/price/maximum) with keyword 'maximum' and parameters {"comparison":"<=","limit":6} received invalid values 10, 15  Diff  interaction did not match schema -34 | - not_ready -35 | price: -36 | type: number -37 |  maximum: 6 [Actual] 10, 15 -38 | minimum: 2 -39 | required: -40 | - id +39 | - not_ready +40 | price: +41 | type: number +42 |  maximum: 6 [Actual] 10, 15 +43 | minimum: 2 +44 | required: +45 | - id $workspace$/openapi.yml +...and 1 endpoint that did not receive traffic -100.0% coverage of your documented operations. 5 requests did not match a documented path (6 total requests). +66.7% coverage of your documented operations. 5 requests did not match a documented path (6 total requests). 9 diffs detected in documented operations New endpoints are only added in interactive mode. Run 'optic capture openapi.yml --update interactive' to add new endpoints @@ -1897,8 +1934,9 @@ GET /books [200 response body] 'author_id' does not match type number. Received 6nTxAFM5ck4Hob77hGQoL (/properties/books/items/properties/author_id) [200 response body] 'status' missing enum value 'hold' (/properties/books/items/properties/status/enum) [200 response body] schema (/properties/books/items/properties/price/maximum) with keyword 'maximum' and parameters {"comparison":"<=","limit":6} received invalid values 10, 15 +...and 1 endpoint that did not receive traffic -100.0% coverage of your documented operations. 5 requests did not match a documented path (6 total requests). +66.7% coverage of your documented operations. 5 requests did not match a documented path (6 total requests). 6 diffs detected in documented operations New endpoints are only added in interactive mode. Run 'optic capture openapi.yml --update interactive' to add new endpoints diff --git a/projects/optic/src/__tests__/integration/workspaces/capture/with-server/openapi.yml b/projects/optic/src/__tests__/integration/workspaces/capture/with-server/openapi.yml index bb64078bb5..a95276b528 100644 --- a/projects/optic/src/__tests__/integration/workspaces/capture/with-server/openapi.yml +++ b/projects/optic/src/__tests__/integration/workspaces/capture/with-server/openapi.yml @@ -5,16 +5,21 @@ info: version: 0.1.0 paths: /books: + post: + "responses": {} get: + # a comment about something responses: "200": description: 200 response content: application/json: schema: + # use the refs here $ref: "#/components/schemas/GetBooks200ResponseBody" components: schemas: + # this is a schema GetBooks200ResponseBody: type: object properties: diff --git a/projects/optic/src/commands/capture/write/file.ts b/projects/optic/src/commands/capture/write/file.ts index 8fc23348e5..8eaffa9eee 100644 --- a/projects/optic/src/commands/capture/write/file.ts +++ b/projects/optic/src/commands/capture/write/file.ts @@ -5,7 +5,7 @@ import { UserError, sourcemapReader, } from '@useoptic/openapi-utilities'; -import { isYaml, loadYaml, writeYaml } from '@useoptic/openapi-io'; +import { applyOperationsToYamlString, isYaml } from '@useoptic/openapi-io'; import jsonpatch, { Operation } from 'fast-json-patch'; import { logger } from '../../../logger'; import chalk from 'chalk'; @@ -31,14 +31,19 @@ export async function writePatchesToFiles( for (let [filePath, operations] of Object.entries(operationsByFile)) { const file = sourcemap.files.find(({ path }) => path === filePath)!; - const parsed = parse(filePath, file.contents); let stringified: string; try { - const patchedContents = jsonpatch.applyPatch( - parsed || {}, - operations - ).newDocument; - stringified = stringify(filePath, patchedContents); + if (isYaml(filePath)) { + stringified = applyOperationsToYamlString(file.contents, operations); + } else { + const parsed = file.contents ? JSON.parse(file.contents) : {}; + const patchedContents = jsonpatch.applyPatch( + parsed || {}, + operations + ).newDocument; + + stringified = JSON.stringify(patchedContents, null, 2); + } } catch (e) { logger.error(''); logger.error( @@ -48,12 +53,12 @@ export async function writePatchesToFiles( location: 'patch files', error: e, operations: JSON.stringify(operations), - parsed: JSON.stringify(parsed), + parsed: file.contents, }); Sentry.captureException(e, { extra: { operations, - parsed, + parsed: file.contents, }, }); throw new UserError(); @@ -62,19 +67,3 @@ export async function writePatchesToFiles( await fs.writeFile(filePath, stringified); } } - -function parse(filePath: string, fileContents: string) { - if (isYaml(filePath)) { - return loadYaml(fileContents); - } else { - return fileContents ? JSON.parse(fileContents) : {}; - } -} - -function stringify(filePath: string, document: any) { - if (isYaml(filePath)) { - return writeYaml(document); - } else { - return JSON.stringify(document, null, 2); - } -}