feat(php): make idempotency key generation IR-driven#17011
feat(php): make idempotency key generation IR-driven#17011devin-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:
|
There was a problem hiding this comment.
AI Review Summary
The PR ports auto-generate idempotency key to the PHP SDK, gated behind a config flag. The core mechanism is sound, but the header-bag reference wiring has a bug that will drop caller-supplied header parameters when both a header parameter block and the idempotency block exist.
- 🔴 1 critical issue(s)
- 🟡 1 warning(s)
| if (!hasExistingHeaderBag) { | ||
| writer.writeTextStatement(`${HEADER_BAG_NAME} = []`); | ||
| } |
There was a problem hiding this comment.
🔴 critical
When hasExistingHeaderBag is true, this block writes into $headers (HEADER_BAG_NAME), but the existing headerParameterCodeBlock populates its own variable (headerParameterBagReference). If those two references differ, the idempotency key lands in a different array than the one holding the caller's header params — and since headerBagReference below prefers the idempotency block's $headers, the header-parameter bag gets dropped from the request entirely.
Either reuse headerParameterCodeBlock.headerParameterBagReference as the target when it exists, or verify they're always the same variable. Otherwise declared header params silently vanish on POST/PUT when the flag is on.
There was a problem hiding this comment.
Addressed in 2a18937. The idempotency block now reuses `headerParameterCodeBlock.headerParameterBagReference` when it exists (both blocks were already writing to the same $headers var, but this makes it explicit and robust) and only declares a fresh bag when there is no existing one — so declared header params can never be dropped.
| writer.write(`${HEADER_BAG_NAME}['${IDEMPOTENCY_KEY_HEADER}'] = `); | ||
| if (declaresIdempotencyKeyHeader) { | ||
| writer.write( | ||
| `$${this.context.getRequestOptionsName()}['headers']['${IDEMPOTENCY_KEY_HEADER}'] ?? ` |
There was a problem hiding this comment.
🟡 warning
This reads $options['headers']['Idempotency-Key'] with an exact-case key, but the declaresIdempotencyKeyHeader check above does a case-insensitive match on the wire name. A header declared as e.g. idempotency-key would pass the check but the caller-wins lookup here uses the hardcoded Idempotency-Key casing, so it won't find a value under the actual declared name. Use the endpoint's declared wire value for the lookup key to stay consistent.
There was a problem hiding this comment.
Addressed in 2a18937. The lookup now uses the endpoint's declared wire value (getWireValue(declaredIdempotencyHeader.name)) for both the emitted header key and the $options['headers'][...] caller-wins lookup, so casing stays consistent with the case-insensitive match. The literal Idempotency-Key is only used for the standalone (non-declared) plain POST/PUT path.
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 |
5ef9ca7 to
cfdd6b4
Compare
Docs Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on
Docs generation runs |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
cfdd6b4 to
a217cd6
Compare
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…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. |
Description
Makes the PHP SDK generator's auto-generated idempotency key IR-driven, mirroring the TypeScript reference (#17007). Instead of the previous per-generator config flag + hardcoded POST/PUT, the generator now reads
ir.sdkConfig.idempotencyKeyGeneration(optional block withheaderNameandmethods). Field present = enabled; absent = disabled.Changes Made
HttpEndpointGenerator: attach the IR-configured header (defaultIdempotency-Key) with a freshly generated UUIDv4 on the IR-configured eligible methods (default POST/PUT). Caller-supplied value wins; method gating and header name come from the IR (no hardcoded POST/PUT).IdempotencyKeycore helper (random_bytes(16)RFC 4122 v4 UUID). Shipped only whensdkConfig.idempotencyKeyGeneration != null(SdkGeneratorContext.getCoreAsIsFiles), so flag-OFF output is byte-identical to main.packages/commons/api-workspace-commons/checkVersionExists.ts: addedgetGeneratorConfigObject()(falls back togeneratorInvocation.configwhenraw.configis absent) used bygetIdempotencyKeyGenerationFromGeneratorConfig()— the shared seed-harness fix, verbatim from the TS branch. +4 unit tests.idempotency-headerssplit intono-custom-config(disabled) andauto-generate-idempotency-key(enabled) variants.Testing
@fern-api/api-workspace-commonssuite green (checkVersionExists.test.ts).pnpm seed test --generator php-sdk --fixture idempotency-headers --local --skip-scripts: 2/2 pass.auto-generate-idempotency-keyvariant,PaymentClient::create(POST):$headers['Idempotency-Key'] = $options['headers']['Idempotency-Key'] ?? IdempotencyKey::generate();(caller-wins + UUID fallback).PaymentClient::delete(DELETE): no injection.no-custom-configvariant: noIdempotencyKeyhelper and no header injection (byte-identical to disabled/main behavior).Link to Devin session: https://app.devin.ai/sessions/ee9a7e8220314b9a832a30a1c0bbfaa6
Requested by: @cadesark