From 521af41bce75d6c4a65a8a0f0e1f8f605a909d52 Mon Sep 17 00:00:00 2001 From: Deep Singhvi Date: Tue, 11 Jun 2024 17:47:15 -0400 Subject: [PATCH] (fix, openapi): fall back to default status code if none provided (#3834) --- .../default-content.test.ts.snap | 131 ++++++++++++++++++ .../src/__test__/default-content.test.ts | 5 + .../fixtures/default-content/openapi.yml | 32 +++++ .../default-content.test.ts.snap | 94 +++++++++++++ .../src/__test__/default-content.test.ts | 5 + .../fixtures/default-content/openapi.yml | 32 +++++ .../v3/converters/endpoint/convertResponse.ts | 67 +++++---- 7 files changed, 341 insertions(+), 25 deletions(-) create mode 100644 packages/cli/openapi-ir-to-fern/src/__test__/__snapshots__/default-content.test.ts.snap create mode 100644 packages/cli/openapi-ir-to-fern/src/__test__/default-content.test.ts create mode 100644 packages/cli/openapi-ir-to-fern/src/__test__/fixtures/default-content/openapi.yml create mode 100644 packages/cli/openapi-parser/src/__test__/__snapshots__/default-content.test.ts.snap create mode 100644 packages/cli/openapi-parser/src/__test__/default-content.test.ts create mode 100644 packages/cli/openapi-parser/src/__test__/fixtures/default-content/openapi.yml diff --git a/packages/cli/openapi-ir-to-fern/src/__test__/__snapshots__/default-content.test.ts.snap b/packages/cli/openapi-ir-to-fern/src/__test__/__snapshots__/default-content.test.ts.snap new file mode 100644 index 00000000000..1a460f10047 --- /dev/null +++ b/packages/cli/openapi-ir-to-fern/src/__test__/__snapshots__/default-content.test.ts.snap @@ -0,0 +1,131 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`open api ir to fern default-content docs 1`] = ` +{ + "definitionFiles": { + "generativelanguage.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "GenerateContent": { + "auth": false, + "docs": undefined, + "examples": [ + { + "path-parameters": { + "model": "model", + }, + "response": { + "body": { + "bar": "bar", + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/generateContent", + "path-parameters": { + "model": "string", + }, + "response": { + "docs": "Successful operation", + "type": "root.GenerateContentResponse", + }, + }, + }, + }, + }, + }, + "packageMarkerFile": { + "types": { + "GenerateContentResponse": { + "docs": undefined, + "properties": { + "bar": "optional", + }, + }, + }, + }, + "rootApiFile": { + "default-environment": "Default", + "display-name": "Default Content Type", + "environments": { + "Default": "https://ai.com", + }, + "error-discrimination": { + "strategy": "status-code", + }, + "name": "api", + }, +} +`; + +exports[`open api ir to fern default-content simple 1`] = ` +{ + "definitionFiles": { + "generativelanguage.yml": { + "imports": { + "root": "__package__.yml", + }, + "service": { + "auth": false, + "base-path": "", + "endpoints": { + "GenerateContent": { + "auth": false, + "docs": undefined, + "examples": [ + { + "path-parameters": { + "model": "model", + }, + "response": { + "body": { + "bar": "bar", + }, + }, + }, + ], + "method": "POST", + "pagination": undefined, + "path": "/generateContent", + "path-parameters": { + "model": "string", + }, + "response": { + "docs": "Successful operation", + "type": "root.GenerateContentResponse", + }, + }, + }, + }, + }, + }, + "packageMarkerFile": { + "types": { + "GenerateContentResponse": { + "docs": undefined, + "properties": { + "bar": "optional", + }, + }, + }, + }, + "rootApiFile": { + "default-environment": "Default", + "display-name": "Default Content Type", + "environments": { + "Default": "https://ai.com", + }, + "error-discrimination": { + "strategy": "status-code", + }, + "name": "api", + }, +} +`; diff --git a/packages/cli/openapi-ir-to-fern/src/__test__/default-content.test.ts b/packages/cli/openapi-ir-to-fern/src/__test__/default-content.test.ts new file mode 100644 index 00000000000..4dbfaef3c07 --- /dev/null +++ b/packages/cli/openapi-ir-to-fern/src/__test__/default-content.test.ts @@ -0,0 +1,5 @@ +import { testConvertOpenAPI } from "./testConvertOpenApi"; + +describe("open api ir to fern", () => { + testConvertOpenAPI("default-content", "openapi.yml"); +}); diff --git a/packages/cli/openapi-ir-to-fern/src/__test__/fixtures/default-content/openapi.yml b/packages/cli/openapi-ir-to-fern/src/__test__/fixtures/default-content/openapi.yml new file mode 100644 index 00000000000..a04104d0ff5 --- /dev/null +++ b/packages/cli/openapi-ir-to-fern/src/__test__/fixtures/default-content/openapi.yml @@ -0,0 +1,32 @@ +--- +openapi: 3.0.3 +info: + title: Default Content Type + version: v1 +servers: +- url: https://ai.com +paths: + "/generateContent": + post: + tags: + - generativelanguage + operationId: GenerateContent + parameters: + - name: model + in: path + required: true + schema: + type: string + responses: + default: + description: Successful operation + content: + application/json: + schema: + "$ref": "#/components/schemas/GenerateContentResponse" +components: + schemas: + GenerateContentResponse: + properties: + bar: + type: string \ No newline at end of file diff --git a/packages/cli/openapi-parser/src/__test__/__snapshots__/default-content.test.ts.snap b/packages/cli/openapi-parser/src/__test__/__snapshots__/default-content.test.ts.snap new file mode 100644 index 00000000000..9c4b2cc2072 --- /dev/null +++ b/packages/cli/openapi-parser/src/__test__/__snapshots__/default-content.test.ts.snap @@ -0,0 +1,94 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`open api parser default-content parse open api 1`] = ` +{ + "basePath": null, + "channel": [], + "description": null, + "endpoints": [ + { + "audiences": [], + "authed": false, + "availability": null, + "description": null, + "errors": {}, + "examples": [], + "generatedRequestName": "GenerateContentRequest", + "headers": [], + "idempotent": null, + "internal": null, + "method": "POST", + "operationId": "GenerateContent", + "pagination": null, + "path": "/generateContent", + "pathParameters": [ + { + "description": null, + "name": "model", + "schema": { + "description": null, + "generatedName": "GenerateContentRequestModel", + "groupName": null, + "nameOverride": null, + "schema": { + "default": null, + "format": null, + "maxLength": null, + "minLength": null, + "pattern": null, + "type": "string", + }, + "type": "primitive", + }, + "variableReference": null, + }, + ], + "queryParameters": [], + "request": null, + "requestNameOverride": null, + "response": { + "description": "Successful operation", + "responseProperty": null, + "schema": { + "description": null, + "generatedName": "GenerateContentResponse", + "groupName": null, + "nameOverride": null, + "schema": "GenerateContentResponse", + "type": "reference", + }, + "type": "json", + }, + "sdkName": null, + "server": [], + "summary": null, + "tags": [ + "generativelanguage", + ], + }, + ], + "globalHeaders": [], + "groups": {}, + "hasEndpointsMarkedInternal": false, + "idempotencyHeaders": [], + "nonRequestReferencedSchemas": [ + "GenerateContentResponse", + ], + "schemas": {}, + "securitySchemes": {}, + "servers": [ + { + "description": null, + "name": null, + "url": "https://ai.com", + }, + ], + "tags": { + "orderedTagIds": null, + "tagsById": {}, + }, + "title": "Default Content Type", + "variables": {}, + "webhooks": [], +} +`; diff --git a/packages/cli/openapi-parser/src/__test__/default-content.test.ts b/packages/cli/openapi-parser/src/__test__/default-content.test.ts new file mode 100644 index 00000000000..e5ef5ce4e58 --- /dev/null +++ b/packages/cli/openapi-parser/src/__test__/default-content.test.ts @@ -0,0 +1,5 @@ +import { testParseOpenAPI } from "./testParseOpenApi"; + +describe("open api parser", () => { + testParseOpenAPI("default-content", "openapi.yml"); +}); diff --git a/packages/cli/openapi-parser/src/__test__/fixtures/default-content/openapi.yml b/packages/cli/openapi-parser/src/__test__/fixtures/default-content/openapi.yml new file mode 100644 index 00000000000..a266b359b1e --- /dev/null +++ b/packages/cli/openapi-parser/src/__test__/fixtures/default-content/openapi.yml @@ -0,0 +1,32 @@ +--- +openapi: 3.0.3 +info: + title: Default Content Type + version: v1 +servers: +- url: https://ai.com +paths: + "/generateContent": + post: + tags: + - generativelanguage + operationId: GenerateContent + parameters: + - name: model + in: path + required: true + schema: + type: string + responses: + default: + description: Successful operation + content: + application/json: + schema: + "$ref": "#/components/schemas/GenerateContentResponse" +types: + schemas: + GenerateContentResponse: + properties: + bar: + type: string \ No newline at end of file diff --git a/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts b/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts index ffa1bd7afbd..51a3b3b3edf 100644 --- a/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts +++ b/packages/cli/openapi-parser/src/openapi/v3/converters/endpoint/convertResponse.ts @@ -40,42 +40,59 @@ export function convertResponse({ return { value: undefined, errors: {} }; } const errors = markErrorSchemas({ responses, context }); + + let successStatusCodePresent = false; + let convertedResponse: FernOpenapiIr.ResponseWithExample | undefined = undefined; for (const statusCode of responseStatusCode != null ? [responseStatusCode] : SUCCESSFUL_STATUS_CODES) { const response = responses[statusCode]; if (response == null) { continue; } + successStatusCodePresent = true; + if (convertedResponse == null) { + convertedResponse = convertResolvedResponse({ + operationContext, + response, + context, + responseBreadcrumbs, + streamFormat + }); + } + } - const convertedResponse = convertResolvedResponse({ + // If no success status codes have been visited, then try to fallback to the `default` status code + if (convertedResponse == null && !successStatusCodePresent && responses.default != null) { + convertedResponse = convertResolvedResponse({ operationContext, - response, + response: responses.default, context, responseBreadcrumbs, streamFormat }); - if (convertedResponse != null) { - switch (convertedResponse.type) { - case "json": - return { - value: convertedResponse, - errors - }; - case "streamingJson": - case "streamingSse": - return { - value: convertedResponse, - errors - }; - case "file": - case "text": - case "streamingText": - return { - value: convertedResponse, - errors - }; - default: - assertNever(convertedResponse); - } + } + + if (convertedResponse != null) { + switch (convertedResponse.type) { + case "json": + return { + value: convertedResponse, + errors + }; + case "streamingJson": + case "streamingSse": + return { + value: convertedResponse, + errors + }; + case "file": + case "text": + case "streamingText": + return { + value: convertedResponse, + errors + }; + default: + assertNever(convertedResponse); } }