Skip to content

fix(typescript, csharp): keep examples for bytes and file upload request bodies - #17463

Open
fern-api[bot] wants to merge 2 commits into
mainfrom
devin/1787076471-keep-bytes-body-examples
Open

fix(typescript, csharp): keep examples for bytes and file upload request bodies#17463
fern-api[bot] wants to merge 2 commits into
mainfrom
devin/1787076471-keep-bytes-body-examples

Conversation

@fern-api

@fern-api fern-api Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

Refs Anduril #22930 (customer report: uploadObject disappeared from the generated JS SDK's reference.md after upgrading to ts-sdk 3.88.0).

exampleOmitsRequestBody, added in #17373 alongside the C# respectOptionalRequestBody work, drops any example where the endpoint declares a request body but example.request == null. FernIr.ExampleRequestBody only has inlinedRequestBody and reference variants — it has no shape for a bytes or fileUpload body — so every example of an upload endpoint reads as bodyless and gets dropped. When the last example goes, SdkGenerator never calls serviceReference.addEndpoint, so the endpoint loses its reference.md section, its @example docstring and its snippets, even though the method is still generated in Client.ts.

The predicate is now restricted to the body types an example can actually carry:

exampleCanCarryRequestBody(endpoint.requestBody) &&   // inlinedRequestBody | reference
    example.request == null &&
    !mayOmitRequestBody({ endpoint, respectOptionalRequestBody })

Filtering of bodyless examples for endpoints with a required (or non-respectOptionalRequestBody-omittable) inline/reference body is unchanged, so the optional-request-body behavior the flag gates keeps working exactly as before. The same one-line copy of the predicate in the C# generator gets the same treatment.

Changes Made

  • generators/typescript/utils/commons/.../getExampleEndpointCalls.ts: only consider inlinedRequestBody / reference bodies when deciding an example omits its request body
  • generators/csharp/sdk/src/utils/exampleUtils.ts: same fix for the C# copy of the predicate
  • Unit test for the predicate: bytes body kept under both flag values, required reference body still dropped, optional reference body kept with respectOptionalRequestBody
  • Changelog entries for the TypeScript and C# SDK generators
  • Updated README.md generator (if applicable)

Testing

  • Unit tests added/updated — generators/typescript/sdk/generator: exampleOmitsRequestBody.test.ts (3) + callOmitsRequestBody.test.ts (4) pass
  • Manual testing completed — before the fix, seed test --generator ts-sdk --fixture bytes-upload --local produced a reference.md containing only # Reference / ## Service (both upload endpoints gone); with the fix, generated output is byte-identical to the checked-in snapshots for ts-sdk bytes-upload, file-upload and both ts-optional-request-body configs, and for csharp-sdk bytes-upload, file-upload and both respect-optional-request-body configs (4/4 seed cases pass, only .fern/metadata.json local-run noise, reverted)

Not verified in this environment: seed post-generation installs and eslint can't run (registry.npmjs.org is off the network allowlist), so those are left to CI.


Open in Devin Review

@nitpickybot nitpickybot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

Narrows exampleOmitsRequestBody to only inline/reference bodies so bytes and file-upload examples aren't silently dropped. The fix is correct and minimal, and the TS and C# copies stay in sync. Only minor nits: missing test coverage for the optional-body/flag-off combination and the now-duplicated predicate in two generators.

  • 🔵 2 suggestion(s)

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread generators/typescript/sdk/generator/src/__test__/exampleOmitsRequestBody.test.ts Outdated
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-08-18T04:12:09Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 97s (n=5) N/A 91s -6s (-6.2%)
ts-sdk square 154s (n=5) 138s (n=5) 119s -35s (-22.7%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-08-18T04:12:09Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-08-18 19:11 UTC

@willkendall01 willkendall01 changed the title fix(typescript): keep examples for bytes and file upload request bodies fix(typescript, csharp): keep examples for bytes and file upload request bodies Aug 20, 2026
@fern-api

fern-api Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up: the restored uploadObject example is still missing its required path parameter

Raising this so it isn't lost — this PR does not fix it, and I don't think it should without a separate decision.

With this branch, the example comes back but renders a call that doesn't compile against the generated signature:

// signature: uploadObject(uploadable: core.file.Uploadable, objectPath: string, ...)
await client.objects.uploadObject(createReadStream("path/to/file"), undefined); // undefined -> required string

C# is the same class of problem (UploadObjectAsync(string objectPath, Stream request, ...) rendered without objectPath).

Where it comes from (verified against the customer's spec): the endpoint's only example is a user-specified one contributed by an override that carries nothing but a curl code sample:

# fern/overrides/objectstore/v1/overrides.yml
paths:
  /api/v1/objects/{objectPath}:
    post:
      x-fern-examples:
        - name: Curl example
          code-samples:
            - sdk: curl
              code: | ...

getExamplesFromExtension turns that into EndpointExample.unknown, and convertPathParameters only populates path parameters when the example declares path-parameters, so the IR example is:

{ "id": "Curl example", "url": "/api/v1/objects/objectPath",
  "endpointPathParameters": [], "request": null }

There is no autogenerated example to fall back on either: generateEndpointExample (v1) bails out with "Bytes request unsupported" for bytes/fileUpload bodies, so a code-sample-only user example is all the snippet renderers get.

Notes on scope:

  • This is independent of respectOptionalRequestBody and of the bodyless-example filter this PR touches — it's about missing path parameter values, not the request body.
  • It pre-dates the filter: the customer's SDK generated on ts-sdk 3.87.3 (before the filter shipped in 3.87.4) contains exactly the same undefined argument. So this PR restores prior behavior, including this wart; it doesn't introduce it.
  • Verified locally for TypeScript by generating from the customer's spec; the C# rendering is inferred from the same IR example shape rather than generated here.

Options, in rough order of preference:

  1. Snippet renderers emit a placeholder for path parameters absent from the example (mirroring how they already placeholder a bytes body with createReadStream("path/to/file")) instead of undefined / a dropped argument.
  2. Backfill autogenerated parameter values when an x-fern-examples entry supplies only code-samples.
  3. Let v1 autogeneration produce examples for bytes/fileUpload endpoints so there is always a complete example to render.

Happy to take (1) as a follow-up PR, or to fold it in here if reviewers prefer — say which and I'll do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant