feat(csharp): read idempotency-key generation config from the IR#17010
feat(csharp): read idempotency-key generation config from the IR#17010devin-ai-integration[bot] wants to merge 4 commits into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
SDK Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on Full benchmark table (click to expand)
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 |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
61bd617 to
9444cd4
Compare
…ve idempotency resolver conflict in favor of main (global api.settings support) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
3 similar comments
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
| var _headers = await new SeedExhaustive.Core.HeadersBuilder.Builder() | ||
| .Add(_client.Options.Headers) | ||
| .Add(_client.Options.AdditionalHeaders) | ||
| .Add("Idempotency-Key", global::System.Guid.NewGuid().ToString()) |
There was a problem hiding this comment.
Can we generate a C# extension method that is called AddIdempotencyHeader(), so the magic string "Idempotency-Key" is in a single location, and the new guid code as well?
This could be a separate file that is generated dynamically.
There was a problem hiding this comment.
Done — generated a dedicated IdempotencyHeaderExtensions core file (emitted only when the IR enables auto-generation) so the "Idempotency-Key" magic string and the new-guid code live in a single generated location:
internal static class IdempotencyHeaderExtensions
{
private const string IdempotencyKeyHeaderName = "Idempotency-Key";
internal static HeadersBuilder.Builder AddIdempotencyHeader(this HeadersBuilder.Builder builder)
{
return builder.Add(IdempotencyKeyHeaderName, global::System.Guid.NewGuid().ToString());
}
}All four request-builder paths (HttpEndpointGenerator, WrappedEndpointRequest, ReferencedEndpointRequest, BytesOnlyEndpointRequest) now call .AddIdempotencyHeader() instead of repeating .Add("Idempotency-Key", global::System.Guid.NewGuid().ToString()). The configured header name (custom names from the IR) is baked into IdempotencyKeyHeaderName. Merge order is preserved (.AddIdempotencyHeader() before the caller/declared headers), so a caller-supplied value still wins.
…IdempotencyHeader helper Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Description
Switches the C# SDK generator from its own
auto-generate-idempotency-keyconfig flag to reading the centralizedsdkConfig.idempotencyKeyGenerationblock from the IR (added + threaded by the CLI in #17018, published as@fern-fern/ir-sdk@67.11.0).The behavior is unchanged from a caller's perspective: when idempotency-key generation is enabled, the generated SDK attaches a UUIDv4 idempotency-key header on eligible HTTP methods, unless the caller supplies one. The difference is where the config lives: enablement, header name, and the eligible-method set now come from the IR instead of being defined per-generator and hardcoded to
POST/PUT. This is the C# generator half of the IR centralization (PR2); #17018 was PR1, and #17007 is the TypeScript reference.Changes Made
GeneratorContext.ts: addedshouldAutoGenerateIdempotencyKey(endpoint)(true iffir.sdkConfig.idempotencyKeyGeneration != nulland.methodsincludes the endpoint's method) andgetIdempotencyKeyGenerationHeaderName()(from.headerName, defaulting toIdempotency-Key). Enablement + eligible methods + header name all come from the IR.HttpEndpointGenerator,WrappedEndpointRequest,ReferencedEndpointRequest,BytesOnlyEndpointRequest): a fallback.Add(<headerName>, global::System.Guid.NewGuid().ToString())emitted before the declared idempotency headers and request-option headers so a caller-supplied value wins.auto-generate-idempotency-keyconfig plumbing (custom-config schema key +autoGenerateIdempotencyKeythreading throughgeneration-info). The CLI now owns theauto-generate-idempotency-keyconfig key and threads it into the IR.@fern-fern/ir-sdkto67.11.0(containsidempotencyKeyGenerationwithmethods) and reconciledpnpm-lock.yaml.fix(seed): made the CLI-only-key threader (getIdempotencyKeyGenerationFromGeneratorConfig) fall back to the resolvedgeneratorInvocation.configwhenrawis absent, so the seed harness's synthetic invocations thread the IR field. Production (whererawis always populated) is unchanged. Added unit tests. (Brought in verbatim from the TS reference branch.)featchangelog entry, reworded to describe the IR-driven approach.Seed fixture proof
idempotency-headersfixture (no-custom-configbaseline +auto-generate-idempotency-key) drives the feature via the CLI key (auto-generate-idempotency-key: true); regenerated flag-on output injects the fallback UUID header on the declared POST endpoint, flag-off has none.exhaustive/auto-generate-idempotency-keyvariant proves the standalone path across the full API..fern/metadata.jsonnormalized to canonical field order withinvokedBy: ci/ciProvider: githubto match sibling committed fixtures.Injection confirmation:
Testing
csharp-base,csharp-codegen,csharp-sdkvitest suites pass; commons idempotency-threading tests pass.pnpm seed test --generator csharp-sdk --fixture idempotency-headers --local --skip-scripts→ 2/2 pass;--fixture exhaustive→ 8/8 pass. biome format clean.Held for Twilio design-doc sign-off before merge.