Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 12 additions & 1 deletion fern.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,17 @@
},
"type": {
"type": "string"
},
"optional": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the endpoint may be called without a request body. When true, an example may\nomit `request`, and the generated SDK lets the caller omit the body. Populated from\n`requestBody.required` when importing from OpenAPI.\n\nThis is distinct from writing `body: optional<Foo>`, which says the body's *value* may\nbe null. Here the body itself may be absent, so the request carries neither content nor\na `Content-Type` header. It maps to `required: false` on the IR's request body reference."
}
},
"required": [
Expand Down Expand Up @@ -2191,7 +2202,7 @@
"type": "null"
}
],
"description": "Whether the endpoint may be called without a request body. When true, an\nexample may omit `request`. Populated from `requestBody.required` when\nimporting from OpenAPI.\n\nThis is read by example validation only: it is not carried into the IR, so it\ndoes not make the request parameter optional in a generated SDK."
"description": "Whether the endpoint may be called without a request body. When true, an\nexample may omit `request`. Populated from `requestBody.required` when\nimporting from OpenAPI.\n\nThis is read by example validation only: it is not carried into the IR, so it\ndoes not make the request parameter optional in a generated SDK.\n\nNote the asymmetry with the identically named field on\n`HttpReferencedRequestBodySchema`, which does reach the IR as `required: false`. An\ninline body has no single type to mark omittable, so its `optional` stays\nvalidation-only."
}
},
"additionalProperties": false
Expand Down
15 changes: 15 additions & 0 deletions fern/apis/fern-definition/definition/service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ types:
- commons.WithDocsSchema
properties:
type: string
optional:
type: optional<boolean>
docs: |
Whether the endpoint may be called without a request body. When true, an example may
omit `request`, and the generated SDK lets the caller omit the body. Populated from
`requestBody.required` when importing from OpenAPI.

This is distinct from writing `body: optional<Foo>`, which says the body's *value* may
be null. Here the body itself may be absent, so the request carries neither content nor
a `Content-Type` header. It maps to `required: false` on the IR's request body reference.

HttpInlineRequestBodySchema:
properties:
Expand All @@ -154,6 +164,11 @@ types:
This is read by example validation only: it is not carried into the IR, so it
does not make the request parameter optional in a generated SDK.

Note the asymmetry with the identically named field on
`HttpReferencedRequestBodySchema`, which does reach the IR as `required: false`. An
inline body has no single type to mark omittable, so its `optional` stays
validation-only.

HttpInlineRequestBodyPropertySchema:
discriminated: false
union:
Expand Down
2 changes: 1 addition & 1 deletion fern/apis/generators-yml/definition/generators.yml
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ types:
docs: Enables respecting forward compatible enums in OpenAPI specifications. Defaults to false.
respect-optional-request-body:
type: optional<boolean>
docs: Enables making a JSON request body that OpenAPI does not mark as required optional in the generated SDK, so callers may omit the body. Defaults to false.
docs: Deprecated and no longer has any effect. A request body that OpenAPI does not mark as required is always described as omittable in the IR, and each SDK generator opts into that behaviour through its own configuration.
use-bytes-for-binary-response:
type: optional<boolean>
docs: Enables using the `bytes` type for binary responsesin OpenAPI specifications. Defaults to a file stream.
Expand Down
4 changes: 2 additions & 2 deletions generators-yml.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2522,7 +2522,7 @@
"type": "null"
}
],
"description": "Enables making a JSON request body that OpenAPI does not mark as required optional in the generated SDK, so callers may omit the body. Defaults to false."
"description": "Deprecated and no longer has any effect. A request body that OpenAPI does not mark as required is always described as omittable in the IR, and each SDK generator opts into that behaviour through its own configuration."
},
"use-bytes-for-binary-response": {
"oneOf": [
Expand Down Expand Up @@ -3204,7 +3204,7 @@
"type": "null"
}
],
"description": "Enables making a JSON request body that OpenAPI does not mark as required optional in the generated SDK, so callers may omit the body. Defaults to false."
"description": "Deprecated and no longer has any effect. A request body that OpenAPI does not mark as required is always described as omittable in the IR, and each SDK generator opts into that behaviour through its own configuration."
},
"use-bytes-for-binary-response": {
"oneOf": [
Expand Down
13 changes: 12 additions & 1 deletion package-yml.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,17 @@
},
"type": {
"type": "string"
},
"optional": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "Whether the endpoint may be called without a request body. When true, an example may\nomit `request`, and the generated SDK lets the caller omit the body. Populated from\n`requestBody.required` when importing from OpenAPI.\n\nThis is distinct from writing `body: optional<Foo>`, which says the body's *value* may\nbe null. Here the body itself may be absent, so the request carries neither content nor\na `Content-Type` header. It maps to `required: false` on the IR's request body reference."
}
},
"required": [
Expand Down Expand Up @@ -2211,7 +2222,7 @@
"type": "null"
}
],
"description": "Whether the endpoint may be called without a request body. When true, an\nexample may omit `request`. Populated from `requestBody.required` when\nimporting from OpenAPI.\n\nThis is read by example validation only: it is not carried into the IR, so it\ndoes not make the request parameter optional in a generated SDK."
"description": "Whether the endpoint may be called without a request body. When true, an\nexample may omit `request`. Populated from `requestBody.required` when\nimporting from OpenAPI.\n\nThis is read by example validation only: it is not carried into the IR, so it\ndoes not make the request parameter optional in a generated SDK.\n\nNote the asymmetry with the identically named field on\n`HttpReferencedRequestBodySchema`, which does reach the IR as `required: false`. An\ninline body has no single type to mark omittable, so its `optional` stays\nvalidation-only."
}
},
"additionalProperties": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ export class RequestBodyConverter extends Converters.AbstractConverters.Abstract
this.queryParameters = queryParameters ?? [];
}

/**
* Absent means required, so only an explicit `requestBody.required: false` is carried through.
* Generators ignore it unless they opt in, which keeps the field additive for existing users.
*/
private get bodyRequired(): boolean | undefined {
return this.required === false ? false : undefined;
}

public convert(): RequestBodyConverter.Output | undefined {
if (this.streamingExtension?.type == "streamCondition") {
return this.convertStreamConditionRequestBody();
Expand Down Expand Up @@ -159,6 +167,7 @@ export class RequestBodyConverter extends Converters.AbstractConverters.Abstract
contentType,
docs: this.description,
requestBodyType: convertedSchema.type,
required: this.bodyRequired,
v2Examples: this.convertMediaTypeObjectExamples({
mediaTypeObject,
exampleGenerationStrategy: "request"
Expand Down Expand Up @@ -196,6 +205,7 @@ export class RequestBodyConverter extends Converters.AbstractConverters.Abstract
contentType,
docs: this.description,
requestBodyType: convertedSchema.type,
required: this.bodyRequired,
v2Examples: this.convertMediaTypeObjectExamples({
mediaTypeObject,
exampleGenerationStrategy: "request"
Expand Down Expand Up @@ -258,6 +268,7 @@ export class RequestBodyConverter extends Converters.AbstractConverters.Abstract
contentType,
docs: this.description,
requestBodyType: TypeReference.unknown(),
required: this.bodyRequired,
v2Examples
}),
streamRequestBody: undefined,
Expand Down Expand Up @@ -525,6 +536,7 @@ export class RequestBodyConverter extends Converters.AbstractConverters.Abstract
contentType,
docs: this.description,
requestBodyType: convertedSchema.type,
required: this.bodyRequired,
v2Examples: this.convertMediaTypeObjectExamples({
mediaTypeObject: modifiedMediaTypeObject,
exampleGenerationStrategy: "request"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@
"method": "POST",
"pagination": undefined,
"path": "/user",
"request": "UserProfile",
"request": {
"body": {
"optional": true,
"type": "UserProfile",
},
},
"response": {
"docs": "Success",
"status-code": 200,
Expand Down Expand Up @@ -95,7 +100,10 @@
method: POST
source:
openapi: ../openapi.yml
request: UserProfile
request:
body:
type: UserProfile
optional: true
response:
docs: Success
type: UserProfile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
"method": "POST",
"pagination": undefined,
"path": "/test",
"request": "OuterOneOf",
"request": {
"body": {
"optional": true,
"type": "OuterOneOf",
},
},
"source": {
"openapi": "../openapi.yml",
},
Expand Down Expand Up @@ -80,7 +85,10 @@
method: POST
source:
openapi: ../openapi.yml
request: OuterOneOf
request:
body:
type: OuterOneOf
optional: true
examples:
- request:
value: value
Expand Down
Loading
Loading