From 189248673a11a8a30538113bac8f7535cc178850 Mon Sep 17 00:00:00 2001 From: Deep Singhvi Date: Thu, 30 Jan 2025 11:11:57 -0600 Subject: [PATCH] fix(cli): support 204 status code with no type (#5781) --- fern.schema.json | 12 ++++-- .../fern-definition/definition/service.yml | 2 +- fern/pages/changelogs/cli/2025-01-30.mdx | 16 +++++++ .../changelogs/ts-express/2025-01-30.mdx | 3 ++ package-yml.schema.json | 12 ++++-- packages/cli/cli/versions.yml | 20 +++++++++ .../types/HttpResponseSchemaDetailed.ts | 2 +- .../types/HttpResponseSchemaDetailed.ts | 4 +- .../ast/visitors/services/visitHttpService.ts | 8 ++-- .../no-response-property.ts | 9 +++- .../validateResponse.ts | 8 +++- .../services/convertHttpResponse.ts | 42 +++++++++++-------- .../src/ir-to-fdr-converter/convertPackage.ts | 9 +++- .../lazy-fern-workspace/src/fern.schema.json | 12 ++++-- .../src/package-yml.schema.json | 12 ++++-- 15 files changed, 127 insertions(+), 44 deletions(-) create mode 100644 fern/pages/changelogs/cli/2025-01-30.mdx create mode 100644 fern/pages/changelogs/ts-express/2025-01-30.mdx diff --git a/fern.schema.json b/fern.schema.json index d3d14b74979..9e8cf0da18a 100644 --- a/fern.schema.json +++ b/fern.schema.json @@ -2000,7 +2000,14 @@ ] }, "type": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "property": { "oneOf": [ @@ -2023,9 +2030,6 @@ ] } }, - "required": [ - "type" - ], "additionalProperties": false }, "service.HttpResponseSchema": { diff --git a/fern/apis/fern-definition/definition/service.yml b/fern/apis/fern-definition/definition/service.yml index e716d7ec77a..5cb4b114b89 100644 --- a/fern/apis/fern-definition/definition/service.yml +++ b/fern/apis/fern-definition/definition/service.yml @@ -144,7 +144,7 @@ types: extends: - commons.WithDocsSchema properties: - type: string + type: optional property: optional status-code: optional diff --git a/fern/pages/changelogs/cli/2025-01-30.mdx b/fern/pages/changelogs/cli/2025-01-30.mdx new file mode 100644 index 00000000000..b8912123096 --- /dev/null +++ b/fern/pages/changelogs/cli/2025-01-30.mdx @@ -0,0 +1,16 @@ +## 0.51.12 +**`(fix):`** The Fern Definition now allows you to declare status codes for the response without having a type. +This is useful for `204` response status codes. + +```yml users.yml +service: + auth: false + base-path: /users + endpoints: + update: + path: "" + response: + status-code: 204 +``` + + diff --git a/fern/pages/changelogs/ts-express/2025-01-30.mdx b/fern/pages/changelogs/ts-express/2025-01-30.mdx new file mode 100644 index 00000000000..1af9526044d --- /dev/null +++ b/fern/pages/changelogs/ts-express/2025-01-30.mdx @@ -0,0 +1,3 @@ +## 0.17.7 +**`(internal):`** Upgrade to IRv55. + diff --git a/package-yml.schema.json b/package-yml.schema.json index 4b42607c97e..b3c06f2f1d4 100644 --- a/package-yml.schema.json +++ b/package-yml.schema.json @@ -2020,7 +2020,14 @@ ] }, "type": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "property": { "oneOf": [ @@ -2043,9 +2050,6 @@ ] } }, - "required": [ - "type" - ], "additionalProperties": false }, "service.HttpResponseSchema": { diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index 1dc4a27f623..3f2531c0a03 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,3 +1,23 @@ +- changelogEntry: + - summary: | + The Fern Definition now allows you to declare status codes for the response without having a type. + This is useful for `204` response status codes. + + ```yml users.yml + service: + auth: false + base-path: /users + endpoints: + update: + path: "" + response: + status-code: 204 + ``` + type: fix + irVersion: 55 + version: 0.51.12 + + - changelogEntry: - summary: | The OpenAPI parser generates response examples that are `{}` for 204 response types. diff --git a/packages/cli/fern-definition/schema/src/schemas/api/resources/service/types/HttpResponseSchemaDetailed.ts b/packages/cli/fern-definition/schema/src/schemas/api/resources/service/types/HttpResponseSchemaDetailed.ts index 289823c2ae6..ceba6838ff4 100644 --- a/packages/cli/fern-definition/schema/src/schemas/api/resources/service/types/HttpResponseSchemaDetailed.ts +++ b/packages/cli/fern-definition/schema/src/schemas/api/resources/service/types/HttpResponseSchemaDetailed.ts @@ -5,7 +5,7 @@ import * as FernDefinition from "../../../index"; export interface HttpResponseSchemaDetailed extends FernDefinition.WithDocsSchema { - type: string; + type?: string; property?: string; "status-code"?: number; } diff --git a/packages/cli/fern-definition/schema/src/schemas/serialization/resources/service/types/HttpResponseSchemaDetailed.ts b/packages/cli/fern-definition/schema/src/schemas/serialization/resources/service/types/HttpResponseSchemaDetailed.ts index 91c5485b63b..54268971057 100644 --- a/packages/cli/fern-definition/schema/src/schemas/serialization/resources/service/types/HttpResponseSchemaDetailed.ts +++ b/packages/cli/fern-definition/schema/src/schemas/serialization/resources/service/types/HttpResponseSchemaDetailed.ts @@ -12,7 +12,7 @@ export const HttpResponseSchemaDetailed: core.serialization.ObjectSchema< FernDefinition.HttpResponseSchemaDetailed > = core.serialization .object({ - type: core.serialization.string(), + type: core.serialization.string().optional(), property: core.serialization.string().optional(), "status-code": core.serialization.number().optional(), }) @@ -20,7 +20,7 @@ export const HttpResponseSchemaDetailed: core.serialization.ObjectSchema< export declare namespace HttpResponseSchemaDetailed { export interface Raw extends WithDocsSchema.Raw { - type: string; + type?: string | null; property?: string | null; "status-code"?: number | null; } diff --git a/packages/cli/fern-definition/validator/src/ast/visitors/services/visitHttpService.ts b/packages/cli/fern-definition/validator/src/ast/visitors/services/visitHttpService.ts index 525b702005c..f8cc340f9bd 100644 --- a/packages/cli/fern-definition/validator/src/ast/visitors/services/visitHttpService.ts +++ b/packages/cli/fern-definition/validator/src/ast/visitors/services/visitHttpService.ts @@ -254,9 +254,11 @@ function visitEndpoint({ visitObject(response, { docs: createDocsVisitor(visitor, nodePathForResponse), type: (type) => { - visitTypeReference(type, [...nodePathForResponse, "type"], { - location: TypeReferenceLocation.Response - }); + if (type != null) { + visitTypeReference(type, [...nodePathForResponse, "type"], { + location: TypeReferenceLocation.Response + }); + } }, property: noop, "status-code": noop diff --git a/packages/cli/fern-definition/validator/src/rules/no-response-property/no-response-property.ts b/packages/cli/fern-definition/validator/src/rules/no-response-property/no-response-property.ts index 219de958b11..fd675e7fcc6 100644 --- a/packages/cli/fern-definition/validator/src/rules/no-response-property/no-response-property.ts +++ b/packages/cli/fern-definition/validator/src/rules/no-response-property/no-response-property.ts @@ -23,6 +23,9 @@ export const NoResponsePropertyRule: Rule = { return []; } const responseType = typeof response === "string" ? response : response.type; + if (responseType == null) { + return []; + } if (parseRawFileType(responseType) != null) { return []; } else if (parseRawTextType(responseType) != null) { @@ -38,8 +41,12 @@ export const NoResponsePropertyRule: Rule = { rootApiFile: workspace.definition.rootApiFile.contents, casingsGenerator: CASINGS_GENERATOR }); + const responseTypeReference = typeof response !== "string" ? response.type : response; + if (responseTypeReference == null) { + return []; + } const resolvedType = typeResolver.resolveTypeOrThrow({ - type: typeof response !== "string" ? response.type : response, + type: responseTypeReference, file }); const result = resolvedTypeHasProperty(resolvedType, responseProperty, file, typeResolver); diff --git a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts index a4bfdd7dd9b..223d20babb8 100644 --- a/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts +++ b/packages/cli/fern-definition/validator/src/rules/valid-example-endpoint-call/validateResponse.ts @@ -69,10 +69,14 @@ function validateBodyResponse({ const violations: RuleViolation[] = []; if (example.error == null) { if (endpoint.response != null) { + const responseTypeReference = + typeof endpoint.response !== "string" ? endpoint.response.type : endpoint.response; + if (responseTypeReference == null) { + return violations; + } violations.push( ...ExampleValidators.validateTypeReferenceExample({ - rawTypeReference: - typeof endpoint.response !== "string" ? endpoint.response.type : endpoint.response, + rawTypeReference: responseTypeReference, example: example.body, typeResolver, exampleResolver, diff --git a/packages/cli/generation/ir-generator/src/converters/services/convertHttpResponse.ts b/packages/cli/generation/ir-generator/src/converters/services/convertHttpResponse.ts index 3830938f170..bbae9af6348 100644 --- a/packages/cli/generation/ir-generator/src/converters/services/convertHttpResponse.ts +++ b/packages/cli/generation/ir-generator/src/converters/services/convertHttpResponse.ts @@ -111,20 +111,22 @@ export function convertNonStreamHttpResponseBody({ const docs = typeof response !== "string" ? response.docs : undefined; const responseType = typeof response === "string" ? response : response.type; - if (parseRawFileType(responseType) != null) { - return HttpResponseBody.fileDownload({ - docs - }); - } else if (parseRawTextType(responseType) != null) { - return HttpResponseBody.text({ - docs - }); - } else if (parseRawBytesType(responseType) != null) { - return HttpResponseBody.bytes({ - docs - }); - } else { - return convertJsonResponse(response, docs, file, typeResolver); + if (responseType != null) { + if (parseRawFileType(responseType) != null) { + return HttpResponseBody.fileDownload({ + docs + }); + } else if (parseRawTextType(responseType) != null) { + return HttpResponseBody.text({ + docs + }); + } else if (parseRawBytesType(responseType) != null) { + return HttpResponseBody.bytes({ + docs + }); + } else { + return convertJsonResponse(response, docs, file, typeResolver); + } } } @@ -173,10 +175,16 @@ function convertJsonResponse( docs: string | undefined, file: FernFileContext, typeResolver: TypeResolver -): HttpResponseBody.Json { - const responseBodyType = file.parseTypeReference(response); +): HttpResponseBody.Json | undefined { + const responseTypeReference = typeof response !== "string" ? response.type : response; + if (responseTypeReference == null) { + return undefined; + } + const responseBodyType = file.parseTypeReference( + typeof response === "string" ? response : { ...response, type: responseTypeReference } + ); const resolvedType = typeResolver.resolveTypeOrThrow({ - type: typeof response !== "string" ? response.type : response, + type: responseTypeReference, file }); const responseProperty = typeof response !== "string" ? response.property : undefined; diff --git a/packages/cli/register/src/ir-to-fdr-converter/convertPackage.ts b/packages/cli/register/src/ir-to-fdr-converter/convertPackage.ts index 1a0bcf27f3f..e01376b5881 100644 --- a/packages/cli/register/src/ir-to-fdr-converter/convertPackage.ts +++ b/packages/cli/register/src/ir-to-fdr-converter/convertPackage.ts @@ -5,6 +5,7 @@ import { generateEndpointExample } from "@fern-api/ir-generator"; import { AutogeneratedEndpointExample, ExampleCodeSample, + IntermediateRepresentation, FernIr as Ir, UserSpecifiedEndpointExample } from "@fern-api/ir-sdk"; @@ -563,6 +564,12 @@ function convertResponse(irResponse: Ir.http.HttpResponse): FdrCjsSdk.api.v1.reg ); if (type != null) { return { type, statusCode: irResponse.statusCode, description }; + } else if (irResponse.statusCode != null) { + return { + statusCode: irResponse.statusCode, + description, + type: { type: "object", extends: [], properties: [], extraProperties: undefined } + }; } else { return undefined; } @@ -570,7 +577,7 @@ function convertResponse(irResponse: Ir.http.HttpResponse): FdrCjsSdk.api.v1.reg function convertResponseErrorsV2( irResponseErrors: Ir.http.ResponseErrors, - ir: Ir.ir.IntermediateRepresentation + ir: IntermediateRepresentation ): FdrCjsSdk.api.v1.register.ErrorDeclarationV2[] { const errors: FdrCjsSdk.api.v1.register.ErrorDeclarationV2[] = []; if (ir.errorDiscriminationStrategy.type === "statusCode") { diff --git a/packages/cli/workspace/lazy-fern-workspace/src/fern.schema.json b/packages/cli/workspace/lazy-fern-workspace/src/fern.schema.json index d3d14b74979..9e8cf0da18a 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/fern.schema.json +++ b/packages/cli/workspace/lazy-fern-workspace/src/fern.schema.json @@ -2000,7 +2000,14 @@ ] }, "type": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "property": { "oneOf": [ @@ -2023,9 +2030,6 @@ ] } }, - "required": [ - "type" - ], "additionalProperties": false }, "service.HttpResponseSchema": { diff --git a/packages/cli/workspace/lazy-fern-workspace/src/package-yml.schema.json b/packages/cli/workspace/lazy-fern-workspace/src/package-yml.schema.json index 4b42607c97e..b3c06f2f1d4 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/package-yml.schema.json +++ b/packages/cli/workspace/lazy-fern-workspace/src/package-yml.schema.json @@ -2020,7 +2020,14 @@ ] }, "type": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "property": { "oneOf": [ @@ -2043,9 +2050,6 @@ ] } }, - "required": [ - "type" - ], "additionalProperties": false }, "service.HttpResponseSchema": {