diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8832995582a5..de7c8bcc0978 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -55,3 +55,9 @@ updates: directory: "/generators/go" schedule: interval: "weekly" + # The CLI generator vendors a Rust SDK whose Cargo.lock ships verbatim into + # every generated CLI, so an advisory here reaches every consumer. + - package-ecosystem: "cargo" + directory: "/generators/cli/sdk" + schedule: + interval: "weekly" diff --git a/generators/cli/changes/0.35.2/refresh-vendored-lock-and-rerunnable-releases.yml b/generators/cli/changes/0.35.2/refresh-vendored-lock-and-rerunnable-releases.yml new file mode 100644 index 000000000000..bd1d4f3e7ce1 --- /dev/null +++ b/generators/cli/changes/0.35.2/refresh-vendored-lock-and-rerunnable-releases.yml @@ -0,0 +1,14 @@ +# yaml-language-server: $schema=../../../../fern-changes-yml.schema.json + +- summary: | + Refresh the vendored Cargo.lock to clear two advisories that shipped into + every generated CLI: h2 0.4.15 -> 0.4.16 (RUSTSEC-2026-0258) and + webbrowser 1.2.1 -> 1.2.4 (RUSTSEC-2026-0257). + type: fix +- summary: | + Make the generated release workflow re-runnable. The final step ran a bare + `gh release create`, so a release that already existed for the tag - cut + from the GitHub UI, or a re-run of a failed `host` job - failed the release + after every build had already succeeded. It now edits and re-uploads into + an existing release, and only creates when there is none. + type: fix \ No newline at end of file diff --git a/generators/cli/changes/0.35.3/auto-propagated-from-rust-0.46.1.yml b/generators/cli/changes/0.35.3/auto-propagated-from-rust-0.46.1.yml new file mode 100644 index 000000000000..b83b18f0b9f9 --- /dev/null +++ b/generators/cli/changes/0.35.3/auto-propagated-from-rust-0.46.1.yml @@ -0,0 +1,3 @@ +- summary: | + Includes Rust SDK Generator 0.46.1 updates. + type: fix diff --git a/generators/cli/changes/0.35.4/auto-propagated-from-rust-0.46.2.yml b/generators/cli/changes/0.35.4/auto-propagated-from-rust-0.46.2.yml new file mode 100644 index 000000000000..a4a879c3f1f5 --- /dev/null +++ b/generators/cli/changes/0.35.4/auto-propagated-from-rust-0.46.2.yml @@ -0,0 +1,3 @@ +- summary: | + Includes Rust SDK Generator 0.46.2 updates. + type: fix diff --git a/generators/cli/sdk/Cargo.lock b/generators/cli/sdk/Cargo.lock index 6f0d922cfa16..d3cb01655227 100644 --- a/generators/cli/sdk/Cargo.lock +++ b/generators/cli/sdk/Cargo.lock @@ -62,7 +62,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -73,7 +73,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -443,6 +443,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.6" @@ -473,7 +483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -713,9 +723,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes", @@ -1283,7 +1293,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1340,6 +1350,28 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + [[package]] name = "objc2-encode" version = "4.1.0" @@ -1354,6 +1386,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags", "objc2", + "objc2-core-foundation", ] [[package]] @@ -1805,7 +1838,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2195,7 +2228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2263,7 +2296,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2835,15 +2868,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2", + "objc2-app-kit", "objc2-foundation", "url", "web-sys", @@ -2855,7 +2888,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/generators/cli/src/__test__/emitReleaseWorkflow.test.ts b/generators/cli/src/__test__/emitReleaseWorkflow.test.ts index d81347b6798d..d8a5363a3b25 100644 --- a/generators/cli/src/__test__/emitReleaseWorkflow.test.ts +++ b/generators/cli/src/__test__/emitReleaseWorkflow.test.ts @@ -59,6 +59,24 @@ describe("constructReleaseWorkflowYaml", () => { expect(yaml).not.toContain("HOMEBREW_TAP_TOKEN"); }); + it("updates an existing GitHub release and overwrites its assets", () => { + const yaml = constructReleaseWorkflowYaml({}); + const releaseStep = yaml.slice(yaml.indexOf("gh release view")); + + expect(releaseStep).toContain('gh release view "${{ needs.plan.outputs.tag }}"'); + expect(releaseStep).toContain("gh release edit"); + expect(releaseStep).toContain("--prerelease=false"); + // A release drafted in the UI must be published, not just filled in — + // and its tag must land on the commit that was actually built. + expect(releaseStep).toContain("--draft=false"); + for (const edit of releaseStep.split("\n").filter((line) => line.includes("gh release edit"))) { + expect(edit).toContain('--target "$RELEASE_COMMIT"'); + } + expect(releaseStep).toContain("gh release upload"); + expect(releaseStep).toContain("--clobber"); + expect(releaseStep).toContain("gh release create"); + }); + describe("with homebrew configured", () => { const yaml = constructReleaseWorkflowYaml({ homebrew: { tap: "acme/homebrew-tap" } }); diff --git a/generators/cli/src/emitReleaseWorkflow.ts b/generators/cli/src/emitReleaseWorkflow.ts index 4a9d580516b1..a4a5b2ea1d78 100644 --- a/generators/cli/src/emitReleaseWorkflow.ts +++ b/generators/cli/src/emitReleaseWorkflow.ts @@ -626,5 +626,20 @@ jobs: # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - gh release create "\${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + # Re-runs and pre-created releases must not fail after a successful build. + # --draft=false publishes a release that was drafted in the UI: + # gh release view finds drafts, so without it the assets upload into + # a release nobody can see and the job still reports success. A draft + # has no tag yet, so --target decides where that tag lands; for an + # already-published release the tag exists and GitHub ignores it. + if gh release view "\${{ needs.plan.outputs.tag }}" > /dev/null 2>&1; then + if [[ -n "$PRERELEASE_FLAG" ]]; then + gh release edit "\${{ needs.plan.outputs.tag }}" --title "$ANNOUNCEMENT_TITLE" --target "$RELEASE_COMMIT" --notes-file "$RUNNER_TEMP/notes.txt" --draft=false --prerelease + else + gh release edit "\${{ needs.plan.outputs.tag }}" --title "$ANNOUNCEMENT_TITLE" --target "$RELEASE_COMMIT" --notes-file "$RUNNER_TEMP/notes.txt" --draft=false --prerelease=false + fi + gh release upload "\${{ needs.plan.outputs.tag }}" artifacts/* --clobber + else + gh release create "\${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + fi `; diff --git a/generators/cli/versions.yml b/generators/cli/versions.yml index 852ab462de51..523a52c8b29d 100644 --- a/generators/cli/versions.yml +++ b/generators/cli/versions.yml @@ -1,4 +1,34 @@ # yaml-language-server: $schema=../../fern-versions-yml.schema.json +- version: 0.35.4 + changelogEntry: + - summary: | + Includes Rust SDK Generator 0.46.2 updates. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 0.35.3 + changelogEntry: + - summary: | + Includes Rust SDK Generator 0.46.1 updates. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 0.35.2 + changelogEntry: + - summary: | + Refresh the vendored Cargo.lock to clear two advisories that shipped into + every generated CLI: h2 0.4.15 -> 0.4.16 (RUSTSEC-2026-0258) and + webbrowser 1.2.1 -> 1.2.4 (RUSTSEC-2026-0257). + type: fix + - summary: | + Make the generated release workflow re-runnable. The final step ran a bare + `gh release create`, so a release that already existed for the tag - cut + from the GitHub UI, or a re-run of a failed `host` job - failed the release + after every build had already succeeded. It now edits and re-uploads into + an existing release, and only creates when there is none. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 0.35.1 changelogEntry: - summary: | diff --git a/generators/csharp/base/src/asIs/BaseRequest.Template.cs b/generators/csharp/base/src/asIs/BaseRequest.Template.cs index e9ab13481cbc..53f9791f6640 100644 --- a/generators/csharp/base/src/asIs/BaseRequest.Template.cs +++ b/generators/csharp/base/src/asIs/BaseRequest.Template.cs @@ -22,6 +22,13 @@ internal abstract record BaseRequest internal Dictionary Headers { get; init; } = new(StringComparer.OrdinalIgnoreCase); internal IRequestOptions? Options { get; init; } +<% if (hasRetriesDisabledEndpoints) { %> + + /// + /// Whether retries are disabled for this request, regardless of the client or request options. + /// + internal bool RetriesDisabled { get; init; } +<% } %> internal abstract HttpContent? CreateContent(); diff --git a/generators/csharp/base/src/asIs/RawClient.Template.cs b/generators/csharp/base/src/asIs/RawClient.Template.cs index 981947ce7ad9..e078a46cf9cb 100644 --- a/generators/csharp/base/src/asIs/RawClient.Template.cs +++ b/generators/csharp/base/src/asIs/RawClient.Template.cs @@ -45,14 +45,15 @@ internal partial class RawClient(ClientOptions clientOptions) var httpRequest = await CreateHttpRequestAsync(request).ConfigureAwait(false); // Send the request. - return await SendWithRetriesAsync(httpRequest, request.Options, cts.Token) + return await SendWithRetriesAsync(httpRequest, request.Options, cts.Token<% if (hasRetriesDisabledEndpoints) { %>, request.RetriesDisabled<% } %>) .ConfigureAwait(false); } internal async global::System.Threading.Tasks.Task.ApiResponse> SendRequestAsync( HttpRequestMessage request, IRequestOptions? options, - CancellationToken cancellationToken = default + CancellationToken cancellationToken = default<% if (hasRetriesDisabledEndpoints) { %>, + bool retriesDisabled = false<% } %> ) { // Apply the request timeout. @@ -61,7 +62,7 @@ internal partial class RawClient(ClientOptions clientOptions) cts.CancelAfter(timeout); // Send the request. - return await SendWithRetriesAsync(request, options, cts.Token).ConfigureAwait(false); + return await SendWithRetriesAsync(request, options, cts.Token<% if (hasRetriesDisabledEndpoints) { %>, retriesDisabled<% } %>).ConfigureAwait(false); } private static async global::System.Threading.Tasks.Task CloneRequestAsync( @@ -142,11 +143,12 @@ internal partial class RawClient(ClientOptions clientOptions) private async global::System.Threading.Tasks.Task.ApiResponse> SendWithRetriesAsync( HttpRequestMessage request, IRequestOptions? options, - CancellationToken cancellationToken + CancellationToken cancellationToken<% if (hasRetriesDisabledEndpoints) { %>, + bool retriesDisabled<% } %> ) { var httpClient = options?.HttpClient ?? Options.HttpClient; - var maxRetries = Math.Max(0, options?.MaxRetries ?? Options.MaxRetries); + var maxRetries = <% if (hasRetriesDisabledEndpoints) { %>retriesDisabled ? 0 : <% } %>Math.Max(0, options?.MaxRetries ?? Options.MaxRetries); var isRetryableContent = IsRetryableContent(request); if (!isRetryableContent || maxRetries == 0) diff --git a/generators/csharp/base/src/asIs/test/RawClientTests/RetriesTests.Template.cs b/generators/csharp/base/src/asIs/test/RawClientTests/RetriesTests.Template.cs index be3b0dcdc78a..5b210e437bd4 100644 --- a/generators/csharp/base/src/asIs/test/RawClientTests/RetriesTests.Template.cs +++ b/generators/csharp/base/src/asIs/test/RawClientTests/RetriesTests.Template.cs @@ -516,6 +516,38 @@ public async SystemTask SendRequestAsync_ShouldRetry_WhenHandlerDisposesRequestC } } +<% if (hasRetriesDisabledEndpoints) { %> + [Test] + public async SystemTask SendRequestAsync_ShouldNotRetry_WhenRetriesDisabledForEndpoint() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .RespondWith( + WireMockResponse + .Create() + .WithStatusCode(<%= retryStatusCodes === "recommended" ? 503 : 500 %>) + .WithBody("Failure") + ); + + var request = new <%= namespaces.qualifiedCore %>.EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Options = new global::<%= namespace%>.RequestOptions { MaxRetries = MaxRetries }, + RetriesDisabled = true, + }; + + var response = await _rawClient.SendRequestAsync(request); + + using (Assert.EnterMultipleScope()) + { + Assert.That(response.StatusCode, Is.EqualTo(<%= retryStatusCodes === "recommended" ? 503 : 500 %>)); + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + } + } +<% } %> + [TearDown] public void TearDown() { diff --git a/generators/csharp/base/src/context/GeneratorContext.ts b/generators/csharp/base/src/context/GeneratorContext.ts index 64d639f47a3b..7d698f6302a5 100644 --- a/generators/csharp/base/src/context/GeneratorContext.ts +++ b/generators/csharp/base/src/context/GeneratorContext.ts @@ -392,6 +392,23 @@ export abstract class GeneratorContext extends AbstractGeneratorContext { ); } + /** + * Checks if the API has any endpoints that disable retries. + * @returns True if the API has any endpoints configured with `retries: { disabled: true }`. + */ + public get hasRetriesDisabledEndpoints(): boolean { + return Object.values(this.ir.services).some((service) => + service.endpoints.some((endpoint) => this.areRetriesDisabled(endpoint)) + ); + } + + /** + * Checks if the endpoint disables retries, regardless of the client or per-request options. + */ + public areRetriesDisabled(endpoint: HttpEndpoint): boolean { + return endpoint.retries?.disabled === true; + } + /** * Checks if the endpoint has a resumable SSE streaming result. */ diff --git a/generators/csharp/base/src/project/CsharpProject.ts b/generators/csharp/base/src/project/CsharpProject.ts index 2de47de09127..90c968d89ae5 100644 --- a/generators/csharp/base/src/project/CsharpProject.ts +++ b/generators/csharp/base/src/project/CsharpProject.ts @@ -637,7 +637,8 @@ dotnet_diagnostic.IDE0005.severity = error context: this.context, namespaces: this.namespaces, clientOptionsRequiredDefaults: this.getClientOptionsRequiredDefaults(), - retryStatusCodes: this.context.settings.retryStatusCodes ?? "legacy" + retryStatusCodes: this.context.settings.retryStatusCodes ?? "legacy", + hasRetriesDisabledEndpoints: this.context.hasRetriesDisabledEndpoints } }); return new File(filename.replace("test/", "").replace(".Template", ""), RelativeFilePath.of(""), rendered); @@ -705,7 +706,8 @@ dotnet_diagnostic.IDE0005.severity = error additionalProperties: true, context: this.context, namespaces: this.namespaces, - retryStatusCodes: this.context.settings.retryStatusCodes ?? "legacy" + retryStatusCodes: this.context.settings.retryStatusCodes ?? "legacy", + hasRetriesDisabledEndpoints: this.context.hasRetriesDisabledEndpoints } }); return new File(filename.replace(".Template", ""), RelativeFilePath.of(""), rendered); diff --git a/generators/csharp/sdk/changes/2.83.1/honor-endpoint-level-retries-disabled.yml b/generators/csharp/sdk/changes/2.83.1/honor-endpoint-level-retries-disabled.yml new file mode 100644 index 000000000000..8c3bedfa7d12 --- /dev/null +++ b/generators/csharp/sdk/changes/2.83.1/honor-endpoint-level-retries-disabled.yml @@ -0,0 +1,5 @@ +- summary: | + Honor the `x-fern-retries: { disabled: true }` extension (`retries: { disabled: true }` in a Fern + definition) at the endpoint level. Requests for those endpoints are now sent exactly once, even when + the client or the per-request options configure retries. + type: fix diff --git a/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts b/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts index 55a6e1c7762f..091d7968b326 100644 --- a/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts +++ b/generators/csharp/sdk/src/endpoint/http/HttpEndpointGenerator.ts @@ -1640,7 +1640,8 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { request: this.csharp.codeblock(this.names.variables.httpRequest), options: this.csharp.codeblock(optionsParamName), clientReference: rawClientReference, - cancellationToken: this.csharp.codeblock(cancellationTokenName) + cancellationToken: this.csharp.codeblock(cancellationTokenName), + retriesDisabled: this.context.areRetriesDisabled(endpoint) }) ); diff --git a/generators/csharp/sdk/src/endpoint/http/RawClient.ts b/generators/csharp/sdk/src/endpoint/http/RawClient.ts index 09993b76718f..de7ddf38a007 100644 --- a/generators/csharp/sdk/src/endpoint/http/RawClient.ts +++ b/generators/csharp/sdk/src/endpoint/http/RawClient.ts @@ -38,6 +38,8 @@ export declare namespace RawClient { request: ast.CodeBlock | ast.ClassInstantiation; /** Cancellation token */ cancellationToken: ast.CodeBlock; + /** whether retries are disabled for the endpoint */ + retriesDisabled?: boolean; } export interface CreateHttpRequestWrapperArgs { @@ -145,6 +147,12 @@ export class RawClient extends WithGeneration { assignment: this.csharp.codeblock(this.names.parameters.requestOptions) }); } + if (this.context.areRetriesDisabled(endpoint)) { + args.push({ + name: "RetriesDisabled", + assignment: this.csharp.codeblock("true") + }); + } switch (requestType) { case "bytes": return { @@ -320,12 +328,18 @@ export class RawClient extends WithGeneration { clientReference, options, request, - cancellationToken + cancellationToken, + retriesDisabled }: RawClient.SendRequestWithHttpRequestArgs): ast.MethodInvocation { return this.csharp.invokeMethod({ on: this.csharp.codeblock(clientReference), method: "SendRequestAsync", - arguments_: [request, options, this.csharp.codeblock(this.names.parameters.cancellationToken)], + arguments_: [ + request, + options, + this.csharp.codeblock(this.names.parameters.cancellationToken), + ...(retriesDisabled === true ? [this.csharp.codeblock("true")] : []) + ], async: true }); } diff --git a/generators/csharp/sdk/versions.yml b/generators/csharp/sdk/versions.yml index 2ee007ab8217..35b638e19e94 100644 --- a/generators/csharp/sdk/versions.yml +++ b/generators/csharp/sdk/versions.yml @@ -1,4 +1,13 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 2.83.1 + changelogEntry: + - summary: | + Honor the `x-fern-retries: { disabled: true }` extension (`retries: { disabled: true }` in a Fern + definition) at the endpoint level. Requests for those endpoints are now sent exactly once, even when + the client or the per-request options configure retries. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 2.83.0 changelogEntry: - summary: | diff --git a/generators/go-v2/sdk/src/endpoint/http/HttpEndpointGenerator.ts b/generators/go-v2/sdk/src/endpoint/http/HttpEndpointGenerator.ts index 299db036f0f3..8a722268b6e2 100644 --- a/generators/go-v2/sdk/src/endpoint/http/HttpEndpointGenerator.ts +++ b/generators/go-v2/sdk/src/endpoint/http/HttpEndpointGenerator.ts @@ -5,6 +5,7 @@ import { FernIr } from "@fern-fern/ir-sdk"; import { getOAuthClientCredentialsScheme, isEndpointSecurity, isPlainStringType } from "../../authUtils.js"; import { SdkGeneratorContext } from "../../SdkGeneratorContext.js"; +import { getDisableRetriesValue } from "../../utils/getDisableRetriesValue.js"; import { AbstractEndpointGenerator } from "../AbstractEndpointGenerator.js"; import { EndpointSignatureInfo } from "../EndpointSignatureInfo.js"; import { getPaginationInfo } from "../utils/getPaginationInfo.js"; @@ -306,7 +307,13 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { { name: "Method", value: go.TypeInstantiation.string(endpoint.method) }, { name: "Headers", value: go.TypeInstantiation.reference(go.codeblock("headers")) }, { name: "MaxAttempts", value: go.TypeInstantiation.reference(go.codeblock("options.MaxAttempts")) }, - { name: "DisableRetries", value: go.TypeInstantiation.reference(go.codeblock("options.DisableRetries")) }, + { + name: "DisableRetries", + value: getDisableRetriesValue({ + endpoint, + whenEnabled: go.TypeInstantiation.reference(go.codeblock("options.DisableRetries")) + }) + }, { name: "BodyProperties", value: go.TypeInstantiation.reference(go.codeblock("options.BodyProperties")) }, { name: "QueryParameters", value: go.TypeInstantiation.reference(go.codeblock("options.QueryParameters")) }, { name: "Client", value: go.TypeInstantiation.reference(go.codeblock("options.HTTPClient")) }, diff --git a/generators/go-v2/sdk/src/endpoint/utils/getPaginationInfo.ts b/generators/go-v2/sdk/src/endpoint/utils/getPaginationInfo.ts index 6112c6827419..6f402903e4ab 100644 --- a/generators/go-v2/sdk/src/endpoint/utils/getPaginationInfo.ts +++ b/generators/go-v2/sdk/src/endpoint/utils/getPaginationInfo.ts @@ -358,7 +358,7 @@ function getReadPageResponseBodyForCursor({ nextPageType: go.Type | undefined; responseType: go.Type; }): go.AstNode { - const doneCondition = getCursorDoneCondition({ context, cursor, pageType, nextPageType }); + const doneCondition = getCursorDoneCondition({ context, cursor }); return go.codeblock((writer) => { writer.write("var zeroValue "); writer.writeNode(nextPageType ?? pageType); @@ -494,32 +494,44 @@ function dereferencesNextCursor({ */ function getCursorDoneCondition({ context, - cursor, - pageType, - nextPageType + cursor }: { context: SdkGeneratorContext; cursor: FernIr.CursorPagination; - pageType: go.Type; - nextPageType: go.Type | undefined; }): string { const zeroValueCondition = "next == zeroValue"; - const isStringCursor = context.isPrimitive({ - typeReference: cursor.next.property.valueType, - primitive: FernIr.PrimitiveTypeV1.String - }); - if (!isStringCursor) { - return zeroValueCondition; - } - const nextType = nextPageType ?? pageType; - if (!nextType.isOptional() || dereferencesNextCursor({ page: cursor.page, cursor: cursor.next })) { - // The cursor is a string value, so its zero value is already the empty string. + if ( + !isNilableStringCursor({ context, cursor: cursor.next }) || + dereferencesNextCursor({ page: cursor.page, cursor: cursor.next }) + ) { return zeroValueCondition; } // Short-circuits before dereferencing a nil cursor. return `${zeroValueCondition} || *next == ""`; } +/** + * Whether the cursor is a string that generates as a Go pointer, so that it can hold an empty + * string in addition to nil. Optionality is resolved through named aliases, which generate as the + * pointer type they alias (e.g. `type Cursor = *string`) rather than as a Go optional. + * + * The IR property is the source of truth for the emitted type because `next` is always declared + * from the response cursor property's getter. + */ +function isNilableStringCursor({ + context, + cursor +}: { + context: SdkGeneratorContext; + cursor: FernIr.ResponseProperty; +}): boolean { + const valueType = cursor.property.valueType; + if (!context.isPrimitive({ typeReference: valueType, primitive: FernIr.PrimitiveTypeV1.String })) { + return false; + } + return context.isOptional(valueType) || context.isNullable(valueType); +} + function getNextResultsSetter({ context, results diff --git a/generators/go-v2/sdk/src/internal/Caller.ts b/generators/go-v2/sdk/src/internal/Caller.ts index 44301749c7b1..24c65a454414 100644 --- a/generators/go-v2/sdk/src/internal/Caller.ts +++ b/generators/go-v2/sdk/src/internal/Caller.ts @@ -2,6 +2,7 @@ import { go } from "@fern-api/go-ast"; import { FernIr } from "@fern-fern/ir-sdk"; import { SdkGeneratorContext } from "../SdkGeneratorContext.js"; +import { getDisableRetriesValue } from "../utils/getDisableRetriesValue.js"; import { mayOmitRequestBody } from "../utils/mayOmitRequestBody.js"; export declare namespace Caller { @@ -150,12 +151,15 @@ export class Caller { }, { name: "DisableRetries", - value: go.TypeInstantiation.reference( - go.selector({ - on: args.optionsReference, - selector: go.codeblock("DisableRetries") - }) - ) + value: getDisableRetriesValue({ + endpoint: args.endpoint, + whenEnabled: go.TypeInstantiation.reference( + go.selector({ + on: args.optionsReference, + selector: go.codeblock("DisableRetries") + }) + ) + }) }, { name: "BodyProperties", diff --git a/generators/go-v2/sdk/src/internal/Streamer.ts b/generators/go-v2/sdk/src/internal/Streamer.ts index 7e3f99fa1c49..cec7fc90daee 100644 --- a/generators/go-v2/sdk/src/internal/Streamer.ts +++ b/generators/go-v2/sdk/src/internal/Streamer.ts @@ -4,6 +4,7 @@ import { go } from "@fern-api/go-ast"; import { FernIr } from "@fern-fern/ir-sdk"; import { SdkGeneratorContext } from "../SdkGeneratorContext.js"; +import { getDisableRetriesValue } from "../utils/getDisableRetriesValue.js"; export declare namespace Streamer { export interface StreamArgs { @@ -95,12 +96,15 @@ export class Streamer { }, { name: "DisableRetries", - value: go.TypeInstantiation.reference( - go.selector({ - on: args.optionsReference, - selector: go.codeblock("DisableRetries") - }) - ) + value: getDisableRetriesValue({ + endpoint: args.endpoint, + whenEnabled: go.TypeInstantiation.reference( + go.selector({ + on: args.optionsReference, + selector: go.codeblock("DisableRetries") + }) + ) + }) }, { name: "BodyProperties", diff --git a/generators/go-v2/sdk/src/utils/__test__/getDisableRetriesValue.test.ts b/generators/go-v2/sdk/src/utils/__test__/getDisableRetriesValue.test.ts new file mode 100644 index 000000000000..d7ca72a50709 --- /dev/null +++ b/generators/go-v2/sdk/src/utils/__test__/getDisableRetriesValue.test.ts @@ -0,0 +1,28 @@ +import { go } from "@fern-api/go-ast"; +import { describe, expect, it } from "vitest"; + +import { getDisableRetriesValue } from "../getDisableRetriesValue.js"; + +const whenEnabled = go.TypeInstantiation.reference(go.codeblock("options.DisableRetries")); + +describe("getDisableRetriesValue", () => { + it("forces retries off when the endpoint disables retries", () => { + expect(getDisableRetriesValue({ endpoint: { retries: { disabled: true } }, whenEnabled })).toEqual( + go.TypeInstantiation.bool(true) + ); + }); + + it("forwards the request options when the endpoint explicitly enables retries", () => { + expect(getDisableRetriesValue({ endpoint: { retries: { disabled: false } }, whenEnabled })).toBe(whenEnabled); + }); + + it("forwards the request options when the endpoint omits the disabled flag", () => { + expect(getDisableRetriesValue({ endpoint: { retries: { disabled: undefined } }, whenEnabled })).toBe( + whenEnabled + ); + }); + + it("forwards the request options when the endpoint has no retries configuration", () => { + expect(getDisableRetriesValue({ endpoint: { retries: undefined }, whenEnabled })).toBe(whenEnabled); + }); +}); diff --git a/generators/go-v2/sdk/src/utils/getDisableRetriesValue.ts b/generators/go-v2/sdk/src/utils/getDisableRetriesValue.ts new file mode 100644 index 000000000000..3f8a999c93ce --- /dev/null +++ b/generators/go-v2/sdk/src/utils/getDisableRetriesValue.ts @@ -0,0 +1,16 @@ +import { go } from "@fern-api/go-ast"; +import { FernIr } from "@fern-fern/ir-sdk"; + +/** + * The `DisableRetries` value for an endpoint's request params. Endpoints that declare retries as disabled are + * issued exactly once, regardless of the client-level and per-request retry options. + */ +export function getDisableRetriesValue({ + endpoint, + whenEnabled +}: { + endpoint: Pick; + whenEnabled: go.TypeInstantiation; +}): go.TypeInstantiation { + return endpoint.retries?.disabled === true ? go.TypeInstantiation.bool(true) : whenEnabled; +} diff --git a/generators/go-v2/sdk/src/wire-tests/WireTestGenerator.ts b/generators/go-v2/sdk/src/wire-tests/WireTestGenerator.ts index 5bb4712c9945..a8cce318ca4a 100644 --- a/generators/go-v2/sdk/src/wire-tests/WireTestGenerator.ts +++ b/generators/go-v2/sdk/src/wire-tests/WireTestGenerator.ts @@ -757,6 +757,32 @@ export class WireTestGenerator { } break; } + case "oauth": { + // The client exchanges these for a token against the mocked token + // endpoint, which produces the Authorization header the stubs require. + if (scheme.configuration.type === "clientCredentials") { + arguments_.push( + go.invokeFunc({ + func: go.typeReference({ + name: "WithClientCredentials", + importPath: this.context.getOptionImportPath() + }), + arguments_: [ + go.codeblock('"test_client_id"'), + go.codeblock('"test_client_secret"') + ], + multiline: false + }) + ); + } + break; + } + case "inferred": + // Credentials are endpoint-specific, so the stubs do not constrain the + // Authorization header. Handled by InferredAuthWireTestGenerator. + break; + default: + assertNever(scheme); } } } diff --git a/generators/go/sdk/changes/1.57.5/fix-oauth-wire-test-client-credentials.yml b/generators/go/sdk/changes/1.57.5/fix-oauth-wire-test-client-credentials.yml new file mode 100644 index 000000000000..ab6a448a6253 --- /dev/null +++ b/generators/go/sdk/changes/1.57.5/fix-oauth-wire-test-client-credentials.yml @@ -0,0 +1,5 @@ +- summary: | + Fixed generated wire tests for OAuth client credentials APIs: the test client is now + constructed with client credentials so requests carry the `Authorization` header the + WireMock stubs require, instead of failing with `Header is not present`. + type: fix diff --git a/generators/go/sdk/changes/1.57.6/fix-aliased-cursor-empty-string-termination.yml b/generators/go/sdk/changes/1.57.6/fix-aliased-cursor-empty-string-termination.yml new file mode 100644 index 000000000000..301e9a04478b --- /dev/null +++ b/generators/go/sdk/changes/1.57.6/fix-aliased-cursor-empty-string-termination.yml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json + +- summary: | + An empty string terminates cursor pagination when the cursor is a named type that resolves to an + optional string (e.g. `type Cursor = *string`), matching an inline optional string. Optionality + is now resolved through aliases, so both spellings of the same type generate the same + termination check. Required string, uuid, int, and datetime cursors are unchanged. + type: fix diff --git a/generators/go/sdk/changes/1.57.7/fix-oauth-token-endpoint-wire-test-stub.yml b/generators/go/sdk/changes/1.57.7/fix-oauth-token-endpoint-wire-test-stub.yml new file mode 100644 index 000000000000..ecd974b6ae84 --- /dev/null +++ b/generators/go/sdk/changes/1.57.7/fix-oauth-token-endpoint-wire-test-stub.yml @@ -0,0 +1,6 @@ +- summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix diff --git a/generators/go/sdk/changes/1.57.8/honor-endpoint-retries-disabled.yml b/generators/go/sdk/changes/1.57.8/honor-endpoint-retries-disabled.yml new file mode 100644 index 000000000000..ae3ee34c01a8 --- /dev/null +++ b/generators/go/sdk/changes/1.57.8/honor-endpoint-retries-disabled.yml @@ -0,0 +1,5 @@ +- summary: | + Honor endpoint-level retry configuration (`x-fern-retries: { disabled: true }`, or `retries: { disabled: true }` + in a Fern definition). Endpoints that disable retries are now issued exactly once, regardless of the + client-level and per-request retry options. + type: fix diff --git a/generators/go/sdk/versions.yml b/generators/go/sdk/versions.yml index 8299c4fbd566..42b4fabe3d2e 100644 --- a/generators/go/sdk/versions.yml +++ b/generators/go/sdk/versions.yml @@ -1,4 +1,42 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 1.57.8 + changelogEntry: + - summary: | + Honor endpoint-level retry configuration (`x-fern-retries: { disabled: true }`, or `retries: { disabled: true }` + in a Fern definition). Endpoints that disable retries are now issued exactly once, regardless of the + client-level and per-request retry options. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 1.57.7 + changelogEntry: + - summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 1.57.6 + changelogEntry: + - summary: | + An empty string terminates cursor pagination when the cursor is a named type that resolves to an + optional string (e.g. `type Cursor = *string`), matching an inline optional string. Optionality + is now resolved through aliases, so both spellings of the same type generate the same + termination check. Required string, uuid, int, and datetime cursors are unchanged. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 1.57.5 + changelogEntry: + - summary: | + Fixed generated wire tests for OAuth client credentials APIs: the test client is now + constructed with client credentials so requests carry the `Authorization` header the + WireMock stubs require, instead of failing with `Header is not present`. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 1.57.4 changelogEntry: - summary: | diff --git a/generators/java/sdk/changes/4.19.1/honor-endpoint-level-retries-disabled.yml b/generators/java/sdk/changes/4.19.1/honor-endpoint-level-retries-disabled.yml new file mode 100644 index 000000000000..8d5003afd281 --- /dev/null +++ b/generators/java/sdk/changes/4.19.1/honor-endpoint-level-retries-disabled.yml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json + +- summary: | + Honor an endpoint's `retries: {disabled: true}` configuration (the `x-fern-retries` OpenAPI + extension). A call to such an endpoint tags its request with a max-retries override of zero, so it + bypasses automatic retries regardless of the client-wide `ClientOptions.maxRetries` or a + per-request `RequestOptions.maxRetries`. Endpoints without the configuration are unchanged. + type: fix diff --git a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractHttpResponseParserGenerator.java b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractHttpResponseParserGenerator.java index 2924b03cd513..d38dd4a1a806 100644 --- a/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractHttpResponseParserGenerator.java +++ b/generators/java/sdk/src/main/java/com/fern/java/client/generators/endpoint/AbstractHttpResponseParserGenerator.java @@ -52,6 +52,7 @@ public abstract class AbstractHttpResponseParserGenerator { private static final String INTEGER_ONE = "1"; private static final String DECIMAL_ONE = "1.0"; + private static final String MAX_RETRIES_OVERRIDE_CLASS_NAME = "MaxRetriesOverride"; /** Helper method to generate diagnostic string for container types. Useful for debugging and error messages. */ private static String getContainerDiagnosticString(com.fern.ir.model.types.ContainerType container) { @@ -154,10 +155,38 @@ public abstract void addResponseHandlingCode( public abstract CodeBlock getNextPageGetter(String endpointName, String methodParameters); + static boolean retriesDisabled(Optional retries) { + if (!retries.isPresent()) { + return false; + } + return retries.get().visit(new RetriesConfiguration.Visitor() { + @Override + public Boolean visit(RetriesDisabledSchema value) { + return value.getDisabled().orElse(false); + } + }); + } + + private ClassName getMaxRetriesOverrideClassName() { + return clientGeneratorContext + .getPoetClassNameFactory() + .getRetryInterceptorClassName() + .nestedClass(MAX_RETRIES_OVERRIDE_CLASS_NAME); + } + + private void addDisabledRetriesTag(CodeBlock.Builder httpResponseBuilder) { + ClassName maxRetriesOverrideClassName = getMaxRetriesOverrideClassName(); + httpResponseBuilder.addStatement( + "$L = $L.newBuilder().tag($T.class, new $T(0)).build()", + variables.getOkhttpRequestName(), + variables.getOkhttpRequestName(), + maxRetriesOverrideClassName, + maxRetriesOverrideClassName); + } + public CodeBlock getResponseParserCodeBlock(MethodSpec.Builder endpointMethodBuilder) { - ClassName retryInterceptorClassName = - clientGeneratorContext.getPoetClassNameFactory().getRetryInterceptorClassName(); - ClassName maxRetriesOverrideClassName = retryInterceptorClassName.nestedClass("MaxRetriesOverride"); + ClassName maxRetriesOverrideClassName = getMaxRetriesOverrideClassName(); + boolean retriesDisabled = retriesDisabled(httpEndpoint.getRetries()); CodeBlock.Builder httpResponseBuilder = CodeBlock.builder() // Default the request client .addStatement( @@ -177,19 +206,24 @@ public CodeBlock getResponseParserCodeBlock(MethodSpec.Builder endpointMethodBui clientOptionsField, generatedClientOptions.httpClientWithTimeout(), AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME) - .endControlFlow() - .beginControlFlow( - "if ($L != null && $L.getMaxRetries().isPresent())", - AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME, - AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME) - .addStatement( - "$L = $L.newBuilder().tag($T.class, new $T($L.getMaxRetries().get())).build()", - variables.getOkhttpRequestName(), - variables.getOkhttpRequestName(), - maxRetriesOverrideClassName, - maxRetriesOverrideClassName, - AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME) .endControlFlow(); + if (retriesDisabled) { + addDisabledRetriesTag(httpResponseBuilder); + } else { + httpResponseBuilder + .beginControlFlow( + "if ($L != null && $L.getMaxRetries().isPresent())", + AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME, + AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME) + .addStatement( + "$L = $L.newBuilder().tag($T.class, new $T($L.getMaxRetries().get())).build()", + variables.getOkhttpRequestName(), + variables.getOkhttpRequestName(), + maxRetriesOverrideClassName, + maxRetriesOverrideClassName, + AbstractEndpointWriterVariableNameContext.REQUEST_OPTIONS_PARAMETER_NAME) + .endControlFlow(); + } if (isStreamingEndpoint()) { httpResponseBuilder.addStatement( "$L = $L.newBuilder().callTimeout(0, $T.SECONDS).build()", @@ -216,6 +250,9 @@ public CodeBlock getResponseParserCodeBlock(MethodSpec.Builder endpointMethodBui public CodeBlock getResponseParserCodeBlockWithoutRequestOptions(MethodSpec.Builder endpointMethodBuilder) { CodeBlock.Builder httpResponseBuilder = CodeBlock.builder(); // Note: OkHttpClient is already initialized by the caller, so we skip that here + if (retriesDisabled(httpEndpoint.getRetries())) { + addDisabledRetriesTag(httpResponseBuilder); + } if (isStreamingEndpoint()) { httpResponseBuilder.addStatement( "$L = $L.newBuilder().callTimeout(0, $T.SECONDS).build()", diff --git a/generators/java/sdk/src/test/java/com/fern/java/client/generators/endpoint/RetriesDisabledTest.java b/generators/java/sdk/src/test/java/com/fern/java/client/generators/endpoint/RetriesDisabledTest.java new file mode 100644 index 000000000000..903f45f24c8c --- /dev/null +++ b/generators/java/sdk/src/test/java/com/fern/java/client/generators/endpoint/RetriesDisabledTest.java @@ -0,0 +1,44 @@ +package com.fern.java.client.generators.endpoint; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fern.ir.model.http.RetriesConfiguration; +import com.fern.ir.model.http.RetriesDisabledSchema; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * Pins when {@link AbstractHttpResponseParserGenerator#retriesDisabled} makes a call bypass automatic retries. Only an + * endpoint that explicitly disables retries opts out, so every other endpoint keeps its existing output. + */ +class RetriesDisabledTest { + + @Test + void noConfiguration_retriesStayEnabled() { + assertThat(AbstractHttpResponseParserGenerator.retriesDisabled(Optional.empty())) + .isFalse(); + } + + @Test + void disabledTrue_retriesAreDisabled() { + assertThat(AbstractHttpResponseParserGenerator.retriesDisabled(retries(Optional.of(true)))) + .isTrue(); + } + + @Test + void disabledFalse_retriesStayEnabled() { + assertThat(AbstractHttpResponseParserGenerator.retriesDisabled(retries(Optional.of(false)))) + .isFalse(); + } + + @Test + void disabledAbsent_retriesStayEnabled() { + assertThat(AbstractHttpResponseParserGenerator.retriesDisabled(retries(Optional.empty()))) + .isFalse(); + } + + private static Optional retries(Optional disabled) { + return Optional.of(RetriesConfiguration.of( + RetriesDisabledSchema.builder().disabled(disabled).build())); + } +} diff --git a/generators/java/sdk/versions.yml b/generators/java/sdk/versions.yml index 99aca23040cc..4579b910c38a 100644 --- a/generators/java/sdk/versions.yml +++ b/generators/java/sdk/versions.yml @@ -1,4 +1,14 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 4.19.1 + changelogEntry: + - summary: | + Honor an endpoint's `retries: {disabled: true}` configuration (the `x-fern-retries` OpenAPI + extension). A call to such an endpoint tags its request with a max-retries override of zero, so it + bypasses automatic retries regardless of the client-wide `ClientOptions.maxRetries` or a + per-request `RequestOptions.maxRetries`. Endpoints without the configuration are unchanged. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 4.19.0 changelogEntry: - summary: | diff --git a/generators/php/sdk/changes/2.20.3/fix-oauth-token-endpoint-wire-test-stub.yml b/generators/php/sdk/changes/2.20.3/fix-oauth-token-endpoint-wire-test-stub.yml new file mode 100644 index 000000000000..ecd974b6ae84 --- /dev/null +++ b/generators/php/sdk/changes/2.20.3/fix-oauth-token-endpoint-wire-test-stub.yml @@ -0,0 +1,6 @@ +- summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix diff --git a/generators/php/sdk/changes/2.20.4/honor-endpoint-level-retries-disabled.yml b/generators/php/sdk/changes/2.20.4/honor-endpoint-level-retries-disabled.yml new file mode 100644 index 000000000000..11f353a79c6a --- /dev/null +++ b/generators/php/sdk/changes/2.20.4/honor-endpoint-level-retries-disabled.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json + +- summary: | + Honor endpoint-level `retries: { disabled: true }` (`x-fern-retries`). Requests for those + endpoints pin `maxRetries` to 0, so they are never retried regardless of the client-level or + per-request retry options. + type: fix diff --git a/generators/php/sdk/src/__test__/retriesDisabled.test.ts b/generators/php/sdk/src/__test__/retriesDisabled.test.ts new file mode 100644 index 000000000000..eab40bc6e2c2 --- /dev/null +++ b/generators/php/sdk/src/__test__/retriesDisabled.test.ts @@ -0,0 +1,44 @@ +import { FernIr } from "@fern-fern/ir-sdk"; +import { describe, expect, it } from "vitest"; + +import { getRetriesDisabledStatement, isRetriesDisabled } from "../endpoint/utils/retriesDisabled.js"; + +const context = { + getRequestOptionsName: () => "options", + getMaxRetriesOptionName: () => "maxRetries" +}; + +function endpoint(retries: FernIr.RetriesConfiguration | undefined): Pick { + return { retries }; +} + +describe("isRetriesDisabled", () => { + it("is true when the endpoint disables retries", () => { + expect(isRetriesDisabled(endpoint({ disabled: true }))).toBe(true); + }); + + it("is false when the endpoint explicitly enables retries", () => { + expect(isRetriesDisabled(endpoint({ disabled: false }))).toBe(false); + }); + + it("is false when the endpoint omits the disabled flag", () => { + expect(isRetriesDisabled(endpoint({ disabled: undefined }))).toBe(false); + }); + + it("is false when the endpoint has no retries configuration", () => { + expect(isRetriesDisabled(endpoint(undefined))).toBe(false); + }); +}); + +describe("getRetriesDisabledStatement", () => { + it("pins maxRetries to 0 when retries are disabled", () => { + expect(getRetriesDisabledStatement({ context, endpoint: endpoint({ disabled: true }) })).toBe( + "$options['maxRetries'] = 0" + ); + }); + + it("emits nothing when retries are not disabled", () => { + expect(getRetriesDisabledStatement({ context, endpoint: endpoint(undefined) })).toBeUndefined(); + expect(getRetriesDisabledStatement({ context, endpoint: endpoint({ disabled: false }) })).toBeUndefined(); + }); +}); diff --git a/generators/php/sdk/src/endpoint/http/HttpEndpointGenerator.ts b/generators/php/sdk/src/endpoint/http/HttpEndpointGenerator.ts index 6b4b4728a751..e5678077ee1e 100644 --- a/generators/php/sdk/src/endpoint/http/HttpEndpointGenerator.ts +++ b/generators/php/sdk/src/endpoint/http/HttpEndpointGenerator.ts @@ -15,6 +15,7 @@ import { SdkGeneratorContext } from "../../SdkGeneratorContext.js"; import { AbstractEndpointGenerator } from "../AbstractEndpointGenerator.js"; import { getEndpointReturnType } from "../utils/getEndpointReturnType.js"; import { mayOmitRequestBody } from "../utils/mayOmitRequestBody.js"; +import { getRetriesDisabledStatement } from "../utils/retriesDisabled.js"; type PagingEndpoint = FernIr.HttpEndpoint & { pagination: NonNullable }; @@ -159,6 +160,8 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { ) ); + this.writeRetriesDisabledOverride({ writer, endpoint }); + this.writeEndpointAuthHeaders({ writer, endpoint }); const queryParameterCodeBlock = endpointSignatureInfo.request?.getQueryParameterCodeBlock(); @@ -228,6 +231,20 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator { }); } + private writeRetriesDisabledOverride({ + writer, + endpoint + }: { + writer: php.Writer; + endpoint: FernIr.HttpEndpoint; + }): void { + const statement = getRetriesDisabledStatement({ context: this.context, endpoint }); + if (statement == null) { + return; + } + writer.writeTextStatement(statement); + } + /** * Under ENDPOINT_SECURITY, computes this endpoint's routed auth headers from the shared * RoutingAuthProvider and merges them into the request options so only the schemes this diff --git a/generators/php/sdk/src/endpoint/utils/retriesDisabled.ts b/generators/php/sdk/src/endpoint/utils/retriesDisabled.ts new file mode 100644 index 000000000000..87396100fa9e --- /dev/null +++ b/generators/php/sdk/src/endpoint/utils/retriesDisabled.ts @@ -0,0 +1,29 @@ +import { FernIr } from "@fern-fern/ir-sdk"; + +import { SdkGeneratorContext } from "../../SdkGeneratorContext.js"; + +/** + * Whether the endpoint declares `retries: { disabled: true }` (the `x-fern-retries` OpenAPI + * extension), meaning its requests must never be retried. + */ +export function isRetriesDisabled(endpoint: Pick): boolean { + return endpoint.retries?.disabled === true; +} + +/** + * The statement that pins the request's maxRetries option to 0, or undefined when the endpoint + * does not disable retries. It is written after the client and request options are merged so it + * overrides both. + */ +export function getRetriesDisabledStatement({ + context, + endpoint +}: { + context: Pick; + endpoint: Pick; +}): string | undefined { + if (!isRetriesDisabled(endpoint)) { + return undefined; + } + return `$${context.getRequestOptionsName()}['${context.getMaxRetriesOptionName()}'] = 0`; +} diff --git a/generators/php/sdk/versions.yml b/generators/php/sdk/versions.yml index 6f1b8906a965..d95b423cfadd 100644 --- a/generators/php/sdk/versions.yml +++ b/generators/php/sdk/versions.yml @@ -1,4 +1,23 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 2.20.4 + changelogEntry: + - summary: | + Honor endpoint-level `retries: { disabled: true }` (`x-fern-retries`). Requests for those + endpoints pin `maxRetries` to 0, so they are never retried regardless of the client-level or + per-request retry options. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 2.20.3 + changelogEntry: + - summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 2.20.2 changelogEntry: - summary: | diff --git a/generators/python-v2/sdk/src/readme/ReadmeSnippetBuilder.ts b/generators/python-v2/sdk/src/readme/ReadmeSnippetBuilder.ts index fdaefeac5f9c..2ed8f53f70ba 100644 --- a/generators/python-v2/sdk/src/readme/ReadmeSnippetBuilder.ts +++ b/generators/python-v2/sdk/src/readme/ReadmeSnippetBuilder.ts @@ -184,7 +184,13 @@ export class ReadmeSnippetBuilder extends AbstractReadmeSnippetBuilder { const methodCallBlock = syncSnippet != null ? this.extractMethodCallFromSyncSnippet(syncSnippet) : undefined; let asyncBody: string; - if (methodCallBlock != null) { + if (this.isStreamingEndpoint(endpoint.endpoint)) { + // Streaming methods are async generator functions, so awaiting the call raises a + // TypeError; the returned stream is iterated with `async for` instead. + const methodCall = this.getMethodCall(endpoint); + const hasParams = this.endpointHasParameters(endpoint.endpoint); + asyncBody = ` async for chunk in ${methodCall}(${hasParams ? "..." : ""}):\n print(chunk)`; + } else if (methodCallBlock != null) { const indentedMethodCall = methodCallBlock .split("\n") .map((line, idx) => { diff --git a/generators/python/core_utilities/shared/http_response.py b/generators/python/core_utilities/shared/http_response.py index 1e45330bd071..35659795b991 100644 --- a/generators/python/core_utilities/shared/http_response.py +++ b/generators/python/core_utilities/shared/http_response.py @@ -22,6 +22,10 @@ def headers(self) -> Dict[str, str]: def status_code(self) -> int: return self._response.status_code + @property + def response(self) -> httpx.Response: + return self._response + class HttpResponse(Generic[T], BaseHttpResponse): """HTTP response wrapper that exposes response headers and data.""" diff --git a/generators/python/core_utilities/shared/stream.py b/generators/python/core_utilities/shared/stream.py new file mode 100644 index 000000000000..d9819300ed8b --- /dev/null +++ b/generators/python/core_utilities/shared/stream.py @@ -0,0 +1,146 @@ +# This file was auto-generated by Fern from our API Definition. + +import types +import typing +from dataclasses import dataclass + +T = typing.TypeVar("T") + + +@dataclass(frozen=True) +class StreamEvent(typing.Generic[T]): + """A single event of a stream, exposing the server-sent event metadata alongside the parsed payload. + + `id`, `event` and `retry` are only populated for server-sent event streams; for JSON and text + streams, which have no event metadata, they are always `None`. + """ + + data: T + id: typing.Optional[str] = None + event: typing.Optional[str] = None + retry: typing.Optional[int] = None + + +class Stream(typing.Generic[T]): + """A stream of parsed events. + + Iterating the stream yields the parsed payloads, while `with_metadata()` yields them wrapped in a + `StreamEvent`, which also exposes the server-sent event `id`, `event` and `retry` fields: + + for chunk in client.service.stream(...): + ... + + for event in client.service.stream(...).with_metadata(): + print(event.id, event.data) + + The HTTP request is only issued once the stream is first iterated. The response is released when + the stream is exhausted, when iterating raises, when `close()` is called, when the stream is used + as a context manager, and when the stream is garbage collected (closing the stream closes the + underlying generator, which in turn closes the response). + """ + + def __init__(self, *, events: typing.Callable[[], typing.Generator[StreamEvent[T], None, None]]): + self._open_events = events + self._events: typing.Optional[typing.Generator[StreamEvent[T], None, None]] = None + self._closed = False + + def with_metadata(self) -> typing.Iterator[StreamEvent[T]]: + return self._events_generator() + + def close(self) -> None: + self._closed = True + events = self._events + self._events = None + if events is not None: + events.close() + + def __iter__(self) -> "Stream[T]": + return self + + def __next__(self) -> T: + return next(self._events_generator()).data + + def __enter__(self) -> "Stream[T]": + return self + + def __exit__( + self, + exc_type: typing.Optional[typing.Type[BaseException]], + exc_value: typing.Optional[BaseException], + traceback: typing.Optional[types.TracebackType], + ) -> None: + self.close() + + def _events_generator(self) -> typing.Generator[StreamEvent[T], None, None]: + if self._closed: + raise RuntimeError("The stream has already been closed.") + if self._events is None: + self._events = self._open_events() + return self._events + + +class AsyncStream(typing.Generic[T]): + """An asynchronous stream of parsed events. + + Iterating the stream yields the parsed payloads, while `with_metadata()` yields them wrapped in a + `StreamEvent`, which also exposes the server-sent event `id`, `event` and `retry` fields: + + async for chunk in client.service.stream(...): + ... + + async for event in (await client.service.stream(...)).with_metadata(): + print(event.id, event.data) + + The HTTP request is only issued once the stream is first iterated. The response is released when + the stream is exhausted, when iterating raises, when `close()` is called, when the stream is used + as an asynchronous context manager, and when the event loop finalizes the underlying asynchronous + generator. + """ + + def __init__(self, *, events: typing.Callable[[], typing.AsyncGenerator[StreamEvent[T], None]]): + self._open_events = events + self._events: typing.Optional[typing.AsyncGenerator[StreamEvent[T], None]] = None + self._closed = False + + def with_metadata(self) -> typing.AsyncIterator[StreamEvent[T]]: + return self._events_generator() + + async def close(self) -> None: + self._closed = True + events = self._events + self._events = None + if events is not None: + await events.aclose() + + async def aclose(self) -> None: + await self.close() + + def __aiter__(self) -> "AsyncStream[T]": + return self + + async def __anext__(self) -> T: + event = await self._events_generator().__anext__() + return event.data + + async def __aenter__(self) -> "AsyncStream[T]": + return self + + async def __aexit__( + self, + exc_type: typing.Optional[typing.Type[BaseException]], + exc_value: typing.Optional[BaseException], + traceback: typing.Optional[types.TracebackType], + ) -> None: + await self.close() + + def __await__(self) -> typing.Generator[None, None, "AsyncStream[T]"]: + """Returns the stream itself, so that it can be awaited before being iterated.""" + yield from () + return self + + def _events_generator(self) -> typing.AsyncGenerator[StreamEvent[T], None]: + if self._closed: + raise RuntimeError("The stream has already been closed.") + if self._events is None: + self._events = self._open_events() + return self._events diff --git a/generators/python/sdk/changes/5.29.0/stream-with-metadata.yml b/generators/python/sdk/changes/5.29.0/stream-with-metadata.yml new file mode 100644 index 000000000000..6031a6cfc9fa --- /dev/null +++ b/generators/python/sdk/changes/5.29.0/stream-with-metadata.yml @@ -0,0 +1,29 @@ +# yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json + +- summary: | + Add `stream_abstraction`. With it enabled, streaming endpoints return a `Stream[T]` + (`AsyncStream[T]` for async clients) rather than a plain iterator, mirroring the TypeScript SDK's + `core.Stream`. Iterating the stream still yields the parsed payloads, while `with_metadata()` + yields them wrapped in a `StreamEvent` that also exposes the server-sent event `id`, `event` and + `retry` fields — `for event in client.completions.stream(...).with_metadata()` — so reading an + event id no longer requires dropping down to `with_raw_response`. The request is still only + issued once the stream is iterated, the stream owns the underlying response and releases it when + exhausted, closed or exited as a (asynchronous) context manager, and the raw client's `data` is + the same stream. `AsyncStream` is awaitable, so `await client.completions.stream(...)` and + `async for chunk in client.completions.stream(...)` both work. Defaults to `false`, so signatures + and snippets are unchanged until you opt in. + type: feat +- summary: | + Server-sent events carrying no data, such as comment-only and retry-only frames, are skipped + instead of being parsed as a payload, which previously failed model construction and logged a + warning. + type: fix +- summary: | + `HttpResponse` and `AsyncHttpResponse` expose the underlying `httpx.Response` through a public + `response` property. + type: feat +- summary: | + Fix the generated async examples for streaming endpoints, which awaited the method call — + `response = await client.completions.stream(...)` — even though it returns an async generator and + raises `TypeError: 'async_generator' object can't be awaited`. + type: fix diff --git a/generators/python/sdk/changes/5.29.1/fix-oauth-token-endpoint-wire-test-stub.yml b/generators/python/sdk/changes/5.29.1/fix-oauth-token-endpoint-wire-test-stub.yml new file mode 100644 index 000000000000..ecd974b6ae84 --- /dev/null +++ b/generators/python/sdk/changes/5.29.1/fix-oauth-token-endpoint-wire-test-stub.yml @@ -0,0 +1,6 @@ +- summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix diff --git a/generators/python/sdk/versions.yml b/generators/python/sdk/versions.yml index ab1b54b4ea8c..98821a4fcd9f 100644 --- a/generators/python/sdk/versions.yml +++ b/generators/python/sdk/versions.yml @@ -1,4 +1,45 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 5.29.1 + changelogEntry: + - summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 5.29.0 + changelogEntry: + - summary: | + Add `stream_abstraction`. With it enabled, streaming endpoints return a `Stream[T]` + (`AsyncStream[T]` for async clients) rather than a plain iterator, mirroring the TypeScript SDK's + `core.Stream`. Iterating the stream still yields the parsed payloads, while `with_metadata()` + yields them wrapped in a `StreamEvent` that also exposes the server-sent event `id`, `event` and + `retry` fields — `for event in client.completions.stream(...).with_metadata()` — so reading an + event id no longer requires dropping down to `with_raw_response`. The request is still only + issued once the stream is iterated, the stream owns the underlying response and releases it when + exhausted, closed or exited as a (asynchronous) context manager, and the raw client's `data` is + the same stream. `AsyncStream` is awaitable, so `await client.completions.stream(...)` and + `async for chunk in client.completions.stream(...)` both work. Defaults to `false`, so signatures + and snippets are unchanged until you opt in. + type: feat + - summary: | + Server-sent events carrying no data, such as comment-only and retry-only frames, are skipped + instead of being parsed as a payload, which previously failed model construction and logged a + warning. + type: fix + - summary: | + `HttpResponse` and `AsyncHttpResponse` expose the underlying `httpx.Response` through a public + `response` property. + type: feat + - summary: | + Fix the generated async examples for streaming endpoints, which awaited the method call — + `response = await client.completions.stream(...)` — even though it returns an async generator and + raises `TypeError: 'async_generator' object can't be awaited`. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 5.28.0 changelogEntry: - summary: | diff --git a/generators/python/src/fern_python/codegen/ast/nodes/type_hint/type_hint.py b/generators/python/src/fern_python/codegen/ast/nodes/type_hint/type_hint.py index 393fed02f7d5..010834d93d7f 100644 --- a/generators/python/src/fern_python/codegen/ast/nodes/type_hint/type_hint.py +++ b/generators/python/src/fern_python/codegen/ast/nodes/type_hint/type_hint.py @@ -113,6 +113,24 @@ def async_iterator(wrapped_type: TypeHint) -> TypeHint: type_parameters=[TypeParameter(wrapped_type)], ) + @staticmethod + def generator(yield_type: TypeHint) -> TypeHint: + return TypeHint( + type=get_reference_to_typing_import("Generator"), + type_parameters=[ + TypeParameter(yield_type), + TypeParameter(TypeHint.none()), + TypeParameter(TypeHint.none()), + ], + ) + + @staticmethod + def async_generator(yield_type: TypeHint) -> TypeHint: + return TypeHint( + type=get_reference_to_typing_import("AsyncGenerator"), + type_parameters=[TypeParameter(yield_type), TypeParameter(TypeHint.none())], + ) + @staticmethod def awaitable(wrapped_type: TypeHint) -> TypeHint: return TypeHint( diff --git a/generators/python/src/fern_python/generators/sdk/client_generator/constants.py b/generators/python/src/fern_python/generators/sdk/client_generator/constants.py index 163b67d4fade..5f74e8034df7 100644 --- a/generators/python/src/fern_python/generators/sdk/client_generator/constants.py +++ b/generators/python/src/fern_python/generators/sdk/client_generator/constants.py @@ -3,6 +3,12 @@ # Variable name used for targets when iterating over streaming or file download responses. CHUNK_VARIABLE: Final[str] = "_chunk" +# Variable name used for targets when iterating over the events of a stream. +EVENT_VARIABLE: Final[str] = "_event" + +# Name of the closure generated for streaming endpoints that produces the stream's events. +EVENTS_FUNCTION_NAME: Final[str] = "_events" + DEFAULT_BODY_PARAMETER_VALUE = "OMIT" RESPONSE_VARIABLE: Final[str] = "_response" diff --git a/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_function_generator.py b/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_function_generator.py index b9ead08f7eb7..41a65446c96b 100644 --- a/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_function_generator.py +++ b/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_function_generator.py @@ -18,6 +18,8 @@ from fern_python.generators.pydantic_model.model_utilities import can_tr_be_fern_model from fern_python.generators.sdk.client_generator.constants import ( CHUNK_VARIABLE, + EVENT_VARIABLE, + EVENTS_FUNCTION_NAME, RESPONSE_VARIABLE, SSE_RECONNECT_VARIABLE, ) @@ -385,13 +387,29 @@ def generate_single_function( snippets=endpoint_snippets or [], ) + @property + def _stream_abstraction(self) -> bool: + return self._context.custom_config.stream_abstraction + + def _raw_data_is_stream(self) -> bool: + return self._stream_abstraction and raw_data_is_stream(self._endpoint) + + def _returns_stream_object(self) -> bool: + return self._stream_abstraction and returns_stream_object(self._endpoint) + def _get_stream_func_return_type(self) -> AST.TypeHint: underlying_type = self._get_response_body_underlying_type( response_body=(self._endpoint.response.body if self._endpoint.response is not None else None), is_async=self._is_async, ) underlying_type_wrapped = ( - AST.TypeHint.async_iterator(underlying_type) if self._is_async else AST.TypeHint.iterator(underlying_type) + self._context.core_utilities.get_stream_type(underlying_type, is_async=self._is_async) + if self._raw_data_is_stream() + else ( + AST.TypeHint.async_iterator(underlying_type) + if self._is_async + else AST.TypeHint.iterator(underlying_type) + ) ) return self._get_http_response_wrapper_type(self._is_async, underlying_type_wrapped) @@ -912,6 +930,17 @@ def _generate_endpoint_snippets( endpoint_snippet = endpoint_snippet_generator.generate_snippet() response_name = "response" endpoint_usage = endpoint_snippet_generator.generate_usage(is_async=is_async, response_name=response_name) + # Async streaming methods are async generator functions, which cannot be awaited, so the + # snippet iterates what they return directly. Methods returning an `AsyncStream` are + # plain functions and the stream is awaitable, so those snippets keep the `await`. + returns_async_generator = ( + is_async + and not self._is_raw_client + and not self._returns_stream_object() + and endpoint.response is not None + and endpoint.response.body is not None + and (endpoint.response.body.get_as_union().type == "streaming" or streaming_parameter == "streaming") + ) # HACK: IR should provide stable ids for example example_id = "default" @@ -928,6 +957,7 @@ def _generate_endpoint_snippets( endpoint_usage=endpoint_usage, generated_root_client=generated_root_client, package=package, + write_await=not returns_async_generator, ) ), ) @@ -943,10 +973,11 @@ def _get_snippet_writer_function_body( endpoint_snippet: AST.Expression, response_name: str, package: ir_types.Package, + write_await: bool = True, ) -> None: if endpoint_usage is not None: writer.write(f"{response_name} = ") - if is_async: + if is_async and write_await: writer.write("await ") writer.write("client.") @@ -968,6 +999,7 @@ def _get_snippet_writer( response_name: str, generated_root_client: GeneratedRootClient, package: ir_types.Package, + write_await: bool = True, ) -> AST.CodeWriter: def write(writer: AST.NodeWriter) -> None: if is_async: @@ -986,6 +1018,7 @@ def write(writer: AST.NodeWriter) -> None: endpoint_snippet=endpoint_snippet, response_name=response_name, package=package, + write_await=write_await, ) writer.write_node(Asyncio.run(AST.Expression("main()")), should_write_as_snippet=False) @@ -997,6 +1030,7 @@ def write(writer: AST.NodeWriter) -> None: endpoint_snippet=endpoint_snippet, response_name=response_name, package=package, + write_await=write_await, ) return AST.CodeWriter(write) @@ -1232,6 +1266,8 @@ def _get_response_body_type( ) non_stream_return = self._get_http_response_wrapper_type(is_async, non_stream_underlying) return AST.TypeHint.union(streaming_type, non_stream_return) + elif self._returns_stream_object(): + streaming_type = self._context.core_utilities.get_stream_type(type_hint, is_async=is_async) else: streaming_type = ( AST.TypeHint.async_iterator(type_hint) if is_async else AST.TypeHint.iterator(type_hint) @@ -1298,8 +1334,10 @@ def _write_response_body_type( json=lambda json_response: self._write_standard_return( writer, response_hint, json_response.get_as_union().docs ), - streaming=lambda stream_response: self._write_yielding_return( - writer, response_hint, stream_response.get_as_union().docs + streaming=lambda stream_response: ( + self._write_standard_return(writer, response_hint, stream_response.get_as_union().docs) + if self._returns_stream_object() and not self._is_raw_client + else self._write_yielding_return(writer, response_hint, stream_response.get_as_union().docs) ), text=lambda t: self._write_standard_return(writer, response_hint, t.docs), stream_parameter=lambda _: None, @@ -1971,6 +2009,66 @@ def _get_http_response_wrapper_type( type_parameters=type_params, ) + def _write_stream_method_body(self, *, writer: AST.NodeWriter, raw_client_invocation: AST.Expression) -> None: + """Write a method body that returns a lazily-opened `Stream` over the raw client's events. + + The events generator is only started once the stream is iterated, which preserves the + behavior of the generator functions these methods used to be: no request is issued (and no + error is raised) until the caller starts consuming the stream. Closing the stream closes the + generator, which exits the raw client's context manager and releases the connection. + """ + response_variable = "r" + event_type = self._context.core_utilities.get_stream_event_type( + self._get_response_body_underlying_type( + response_body=self._endpoint.response.body if self._endpoint.response is not None else None, + is_async=self._is_async, + ) + ) + events_expression = f"{response_variable}.data.with_metadata()" + if self._is_async: + with_body: List[AST.AstNode] = [ + AST.ForStatement( + target=EVENT_VARIABLE, + iterable=AST.Expression(events_expression), + is_async=True, + body=[AST.YieldStatement(AST.Expression(EVENT_VARIABLE))], + ) + ] + else: + with_body = [AST.YieldStatement(AST.Expression(events_expression), is_yield_from=True)] + + writer.write_node( + AST.FunctionDeclaration( + name=EVENTS_FUNCTION_NAME, + is_async=self._is_async, + signature=AST.FunctionSignature( + return_type=AST.TypeHint.async_generator(event_type) + if self._is_async + else AST.TypeHint.generator(event_type) + ), + body=[ + AST.WithStatement( + context_managers=[ + AST.WithContextManager( + expression=raw_client_invocation, + as_variable=response_variable, + ) + ], + body=with_body, + is_async=self._is_async, + ) + ], + ) + ) + writer.write_node( + AST.ReturnStatement( + self._context.core_utilities.instantiate_stream( + events=AST.Expression(EVENTS_FUNCTION_NAME), + is_async=self._is_async, + ) + ) + ) + def generate_wrapper_function(self) -> AST.FunctionDeclaration: """Create a wrapper method that delegates to the raw client and extracts the data property.""" @@ -1995,7 +2093,9 @@ def write_method_body(writer: AST.NodeWriter) -> None: ) ) data_attribute = "data" - if is_streaming_endpoint(self._endpoint): + if self._returns_stream_object(): + self._write_stream_method_body(writer=writer, raw_client_invocation=func_invocation_expr) + elif is_streaming_endpoint(self._endpoint): response_variable = "r" body: list[AST.AstNode] = [] if self._is_async: @@ -2049,13 +2149,41 @@ def write_method_body(writer: AST.NodeWriter) -> None: return AST.FunctionDeclaration( name=get_endpoint_name(self._endpoint), - is_async=self._is_async, + # Methods returning an `AsyncStream` are plain functions, so that the returned stream can + # be consumed with `async for` directly as well as awaited (`AsyncStream` is awaitable). + is_async=self._is_async and not self._returns_stream_object(), signature=function.signature, docstring=function.docstring, body=AST.CodeWriter(write_method_body), ) +def raw_data_is_stream(endpoint: ir_types.HttpEndpoint) -> bool: + """Whether the raw client's `data` would be a `Stream` (SSE, JSON lines and text streams). + + Only relevant when `stream_abstraction` is enabled; file downloads keep yielding raw `bytes` + chunks either way. + """ + return ( + endpoint.response is not None + and endpoint.response.body is not None + and endpoint.response.body.get_as_union().type in ("streaming", "streamParameter") + ) + + +def returns_stream_object(endpoint: ir_types.HttpEndpoint) -> bool: + """Whether the endpoint's public method would return the `Stream` itself. + + Only relevant when `stream_abstraction` is enabled. Stream-condition endpoints keep returning an + iterator, because their overloads are generated through a separate code path. + """ + return ( + endpoint.response is not None + and endpoint.response.body is not None + and endpoint.response.body.get_as_union().type == "streaming" + ) + + def is_streaming_endpoint(endpoint: ir_types.HttpEndpoint) -> bool: return bool( endpoint.response is not None diff --git a/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_response_code_writer.py b/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_response_code_writer.py index 98e2b8c1094a..dba897a21eac 100644 --- a/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_response_code_writer.py +++ b/generators/python/src/fern_python/generators/sdk/client_generator/endpoint_response_code_writer.py @@ -104,6 +104,34 @@ def _instantiate_http_response( ], ) + @property + def _stream_abstraction(self) -> bool: + return self._context.custom_config.stream_abstraction + + def _wrap_in_stream_event(self, *, data: AST.Expression, from_sse: bool) -> AST.Expression: + """Wrap a parsed payload in a `StreamEvent`, carrying the server-sent event metadata along. + + Without `stream_abstraction` the payload is yielded on its own, as it always was. + """ + if not self._stream_abstraction: + return data + kwargs = [("data", data)] + if from_sse: + sse = EndpointResponseCodeWriter.SSE_VARIABLE + kwargs.extend( + [ + ("id", AST.Expression(f"{sse}.id or None")), + ("event", AST.Expression(f"{sse}.event")), + ("retry", AST.Expression(f"{sse}.retry")), + ] + ) + return AST.Expression( + AST.ClassInstantiation( + class_=self._context.core_utilities.get_stream_event_reference(), + kwargs=kwargs, + ) + ) + def _handle_success_stream(self, *, writer: AST.NodeWriter, stream_response: ir_types.StreamingResponse) -> None: iter_func_body = [] @@ -201,6 +229,19 @@ def _handle_success_stream(self, *, writer: AST.NodeWriter, stream_response: ir_ ], else_code=None, ), + # Comment-only and retry-only frames are legal SSE and carry no payload, + # so they are skipped instead of being parsed as a model. + AST.ConditionalTree( + conditions=[ + AST.IfConditionLeaf( + condition=AST.Expression( + f"len({EndpointResponseCodeWriter.SSE_VARIABLE}.data) == 0" + ), + code=[AST.ContinueStatement()], + ), + ], + else_code=None, + ), ] + sse_for_body, is_async=self._is_async, @@ -237,9 +278,14 @@ def _handle_success_stream(self, *, writer: AST.NodeWriter, stream_response: ir_ else_code=None, ), AST.YieldStatement( - self._context.core_utilities.get_construct( - self._get_streaming_response_data_type(stream_response), - AST.Expression(Json.loads(AST.Expression(EndpointResponseCodeWriter.STREAM_TEXT_VARIABLE))), + self._wrap_in_stream_event( + data=self._context.core_utilities.get_construct( + self._get_streaming_response_data_type(stream_response), + AST.Expression( + Json.loads(AST.Expression(EndpointResponseCodeWriter.STREAM_TEXT_VARIABLE)) + ), + ), + from_sse=False, ) ), ] @@ -271,29 +317,42 @@ def _handle_success_stream(self, *, writer: AST.NodeWriter, stream_response: ir_ iter_func_body.append(AST.ReturnStatement()) + data: AST.Expression + if self._stream_abstraction: + event_type = self._context.core_utilities.get_stream_event_type( + self._get_streaming_response_data_type(stream_response) + ) + iter_signature = AST.FunctionSignature( + return_type=AST.TypeHint.async_generator(event_type) + if self._is_async + else AST.TypeHint.generator(event_type) + ) + # The stream starts `_iter` itself, so nothing is consumed until the stream is iterated. + data = self._context.core_utilities.instantiate_stream( + events=AST.Expression("_iter"), + is_async=self._is_async, + ) + else: + iter_signature = AST.FunctionSignature() + data = AST.Expression( + AST.FunctionInvocation( + function_definition=AST.Reference( + qualified_name_excluding_import=("_iter",), + ), + args=[], + ) + ) + writer.write_node( AST.FunctionDeclaration( name="_iter", - signature=AST.FunctionSignature(), + signature=iter_signature, body=iter_func_body, is_async=self._is_async, ) ) - writer.write_node( - AST.ReturnStatement( - self._instantiate_http_response( - data=AST.Expression( - AST.FunctionInvocation( - function_definition=AST.Reference( - qualified_name_excluding_import=("_iter",), - ), - args=[], - ) - ) - ) - ) - ) + writer.write_node(AST.ReturnStatement(self._instantiate_http_response(data=data))) def _get_iter_lines_method(self, *, is_async: bool) -> str: if is_async: @@ -908,9 +967,12 @@ def _build_data_level_sse_body( AST.TryStatement( body=[ AST.YieldStatement( - self._context.core_utilities.get_construct_sse( - self._get_streaming_response_data_type(stream_response), - AST.Expression(f"{EndpointResponseCodeWriter.SSE_VARIABLE}"), + self._wrap_in_stream_event( + data=self._context.core_utilities.get_construct_sse( + self._get_streaming_response_data_type(stream_response), + AST.Expression(f"{EndpointResponseCodeWriter.SSE_VARIABLE}"), + ), + from_sse=True, ), ), ], @@ -1002,7 +1064,7 @@ def _build_protocol_level_sse_body( condition=AST.Expression(f"{EndpointResponseCodeWriter.SSE_VARIABLE}.event == {repr(wire_value)}"), code=[ AST.TryStatement( - body=[AST.YieldStatement(yield_expr)], + body=[AST.YieldStatement(self._wrap_in_stream_event(data=yield_expr, from_sse=True))], handlers=[ AST.ExceptHandler( body=[ diff --git a/generators/python/src/fern_python/generators/sdk/context/sdk_generator_context.py b/generators/python/src/fern_python/generators/sdk/context/sdk_generator_context.py index a131b502ac2f..c426ce6fac21 100644 --- a/generators/python/src/fern_python/generators/sdk/context/sdk_generator_context.py +++ b/generators/python/src/fern_python/generators/sdk/context/sdk_generator_context.py @@ -66,6 +66,13 @@ def __init__( for webhook_group in ir.webhook_groups.values() for webhook in webhook_group ) + _has_streaming_endpoints = any( + ep.response is not None + and ep.response.body is not None + and ep.response.body.get_as_union().type in ("streaming", "streamParameter") + for service in ir.services.values() + for ep in service.endpoints + ) _idempotency_key_generation = ir.sdk_config.idempotency_key_generation _generates_idempotency_key = _idempotency_key_generation is not None and any( ep.method in _idempotency_key_generation.methods @@ -79,6 +86,7 @@ def __init__( project_module_path=project_module_path, custom_config=custom_config, has_webhook_signature_verification=_has_webhook_signature_verification, + has_streaming_endpoints=_has_streaming_endpoints, ) self.custom_config = custom_config self.source_file_factory = SourceFileFactory(should_format=not custom_config.skip_formatting) diff --git a/generators/python/src/fern_python/generators/sdk/core_utilities/core_utilities.py b/generators/python/src/fern_python/generators/sdk/core_utilities/core_utilities.py index f2680d1b81b2..d2df65ca5647 100644 --- a/generators/python/src/fern_python/generators/sdk/core_utilities/core_utilities.py +++ b/generators/python/src/fern_python/generators/sdk/core_utilities/core_utilities.py @@ -31,6 +31,7 @@ def __init__( custom_config: SDKCustomConfig, has_webhook_signature_verification: bool = False, generates_idempotency_key: bool = False, + has_streaming_endpoints: bool = False, ) -> None: self.filepath = (Filepath.DirectoryFilepathPart(module_name="core"),) self._module_path = tuple(part.module_name for part in self.filepath) @@ -54,6 +55,8 @@ def __init__( self._default_max_retries = custom_config.default_max_retries self._retry_status_codes = custom_config.retry_status_codes self._has_webhook_signature_verification = has_webhook_signature_verification + self._stream_abstraction = custom_config.stream_abstraction + self._has_streaming_endpoints = has_streaming_endpoints def copy_to_project(self, *, project: Project) -> None: datetime_replacements = ( @@ -201,6 +204,19 @@ def copy_to_project(self, *, project: Project) -> None: exports={"HttpResponse", "AsyncHttpResponse"} if not self._exclude_types_from_init_exports else set(), ) + if self._has_streaming_endpoints and self._stream_abstraction: + self._copy_file_to_project( + project=project, + relative_filepath_on_disk="stream.py", + filepath_in_project=Filepath( + directories=self.filepath, + file=Filepath.FilepathPart(module_name="stream"), + ), + exports={"AsyncStream", "Stream", "StreamEvent"} + if not self._exclude_types_from_init_exports + else set(), + ) + self._copy_file_to_project( project=project, relative_filepath_on_disk="force_multipart.py", @@ -882,6 +898,45 @@ def get_update_forward_refs(self) -> AST.Reference: ), ) + def get_stream_reference(self, is_async: bool) -> AST.ClassReference: + return AST.ClassReference( + qualified_name_excluding_import=(), + import_=AST.ReferenceImport( + module=AST.Module.local(*self._module_path, "stream"), + named_import="AsyncStream" if is_async else "Stream", + ), + ) + + def get_stream_type(self, inner_type: AST.TypeHint, is_async: bool) -> AST.TypeHint: + return AST.TypeHint( + type=self.get_stream_reference(is_async), + type_parameters=[AST.TypeParameter(inner_type)], + ) + + def get_stream_event_reference(self) -> AST.ClassReference: + return AST.ClassReference( + qualified_name_excluding_import=(), + import_=AST.ReferenceImport( + module=AST.Module.local(*self._module_path, "stream"), + named_import="StreamEvent", + ), + ) + + def get_stream_event_type(self, inner_type: AST.TypeHint) -> AST.TypeHint: + return AST.TypeHint( + type=self.get_stream_event_reference(), + type_parameters=[AST.TypeParameter(inner_type)], + ) + + def instantiate_stream(self, events: AST.Expression, is_async: bool) -> AST.Expression: + return AST.Expression( + AST.ClassInstantiation( + class_=self.get_stream_reference(is_async), + args=[], + kwargs=[("events", events)], + ) + ) + def get_paginator_reference(self, is_async: bool) -> AST.ClassReference: return AST.ClassReference( qualified_name_excluding_import=(), diff --git a/generators/python/src/fern_python/generators/sdk/custom_config.py b/generators/python/src/fern_python/generators/sdk/custom_config.py index 2a3eaa765661..cbeb3a0adb68 100644 --- a/generators/python/src/fern_python/generators/sdk/custom_config.py +++ b/generators/python/src/fern_python/generators/sdk/custom_config.py @@ -132,6 +132,12 @@ class SDKCustomConfig(pydantic.BaseModel): # this is opted in. respect_optional_request_body: bool = False + # When True, streaming endpoints return a `Stream` (`AsyncStream` for async clients) instead of + # a plain iterator. The stream still iterates as the parsed payloads, and `with_metadata()` + # additionally exposes the server-sent event `id`, `event` and `retry` fields. Off by default — + # existing output is unchanged unless this is opted in. + stream_abstraction: bool = False + # If true, treats path parameters as named parameters in endpoint functions inline_path_params: bool = False diff --git a/generators/ruby-v2/base/src/__test__/appendAppInfoToUserAgent.test.ts b/generators/ruby-v2/base/src/__test__/appendAppInfoToUserAgent.test.ts index e2b4b864cbee..b3bcad7bec56 100644 --- a/generators/ruby-v2/base/src/__test__/appendAppInfoToUserAgent.test.ts +++ b/generators/ruby-v2/base/src/__test__/appendAppInfoToUserAgent.test.ts @@ -38,7 +38,8 @@ function extractEmittedHelperSource(): string { includePlatformHeaders: false, allowUserAgentAppInfo: true, defaultMaxRetries: 2, - endpointSecurity: false + endpointSecurity: false, + requestLevelMaxRetries: false }) .replace(/\{\{RETRY_STATUS_CODES_ARRAY\}\}/g, "[].freeze"); diff --git a/generators/ruby-v2/base/src/asIs/internal/http/base_request.Template.rb b/generators/ruby-v2/base/src/asIs/internal/http/base_request.Template.rb index adcd3d793e3f..e348d2cd7fb8 100644 --- a/generators/ruby-v2/base/src/asIs/internal/http/base_request.Template.rb +++ b/generators/ruby-v2/base/src/asIs/internal/http/base_request.Template.rb @@ -5,7 +5,7 @@ module Internal module Http # @api private class BaseRequest - attr_reader :base_url, :path, :method, :headers, :query, :request_options + attr_reader :base_url, :path, :method, :headers, :query, :request_options<% if (requestLevelMaxRetries) { %>, :max_retries<% } %> # @param base_url [String] The base URL for the request # @param path [String] The path for the request @@ -13,14 +13,16 @@ class BaseRequest # @param headers [Hash] Additional headers for the request (optional) # @param query [Hash] Query parameters for the request (optional) # @param request_options [<%= gem_namespace %>::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {}) +<% if (requestLevelMaxRetries) { %> # @param max_retries [Integer, nil] Overrides the client's retry count for this request +<% } %> def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {}<% if (requestLevelMaxRetries) { %>, max_retries: nil<% } %>) @base_url = base_url @path = path @method = method @headers = headers @query = query @request_options = request_options - end +<% if (requestLevelMaxRetries) { %> @max_retries = max_retries +<% } %> end # @return [Hash] The query parameters merged with additional query parameters from request options. def encode_query diff --git a/generators/ruby-v2/base/src/asIs/internal/http/raw_client.Template.rb b/generators/ruby-v2/base/src/asIs/internal/http/raw_client.Template.rb index 84defd0c959b..fff810fe408a 100644 --- a/generators/ruby-v2/base/src/asIs/internal/http/raw_client.Template.rb +++ b/generators/ruby-v2/base/src/asIs/internal/http/raw_client.Template.rb @@ -181,7 +181,7 @@ def send(request) response = conn.request(http_request) - break unless should_retry?(response, attempt) + break unless should_retry?(response, attempt<% if (requestLevelMaxRetries) { %>, max_retries: request.max_retries<% } %>) delay = retry_delay(response, attempt) sleep(delay) @@ -203,9 +203,11 @@ def protected_header_keys # Determines if a request should be retried based on the response status code. # @param response [Net::HTTPResponse] The HTTP response. # @param attempt [Integer] The current retry attempt (0-indexed). - # @return [Boolean] Whether the request should be retried. - def should_retry?(response, attempt) - return false if attempt >= @max_retries +<% if (requestLevelMaxRetries) { %> # @param max_retries [Integer, nil] The request-level retry count, when the endpoint + # overrides the client-level setting. +<% } %> # @return [Boolean] Whether the request should be retried. + def should_retry?(response, attempt<% if (requestLevelMaxRetries) { %>, max_retries: nil<% } %>) + return false if attempt >= <% if (requestLevelMaxRetries) { %>(max_retries || @max_retries)<% } else { %>@max_retries<% } %> status = response.code.to_i RETRYABLE_STATUSES.include?(status) diff --git a/generators/ruby-v2/base/src/asIs/internal/json/request.Template.rb b/generators/ruby-v2/base/src/asIs/internal/json/request.Template.rb index 6b3638f09881..7dea7374f573 100644 --- a/generators/ruby-v2/base/src/asIs/internal/json/request.Template.rb +++ b/generators/ruby-v2/base/src/asIs/internal/json/request.Template.rb @@ -16,8 +16,9 @@ class Request < <%= gem_namespace %>::Internal::Http::BaseRequest <% if (respectOptionalRequestBody) { %> # @param omit_content_type_without_body [Boolean] When true and no body is present, the # Content-Type header is omitted (used for endpoints whose request body is optional) <% } %> # @param request_options [<%= gem_namespace %>::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, <% if (respectOptionalRequestBody) { %>omit_content_type_without_body: false, <% } %>request_options: {}) - super(base_url:, path:, method:, headers:, query:, request_options:) +<% if (requestLevelMaxRetries) { %> # @param max_retries [Integer, nil] Overrides the client's retry count for this request +<% } %> def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, <% if (respectOptionalRequestBody) { %>omit_content_type_without_body: false, <% } %>request_options: {}<% if (requestLevelMaxRetries) { %>, max_retries: nil<% } %>) + super(base_url:, path:, method:, headers:, query:, request_options:<% if (requestLevelMaxRetries) { %>, max_retries:<% } %>) @body = body <% if (respectOptionalRequestBody) { %> @omit_content_type_without_body = omit_content_type_without_body diff --git a/generators/ruby-v2/base/src/asIs/internal/multipart/multipart_request.Template.rb b/generators/ruby-v2/base/src/asIs/internal/multipart/multipart_request.Template.rb index 4087019b55b5..6bd987e474da 100644 --- a/generators/ruby-v2/base/src/asIs/internal/multipart/multipart_request.Template.rb +++ b/generators/ruby-v2/base/src/asIs/internal/multipart/multipart_request.Template.rb @@ -14,8 +14,9 @@ class Request < <%= gem_namespace %>::Internal::Http::BaseRequest # @param query [Hash] Query parameters for the request (optional) # @param body [MultipartFormData, nil] The multipart form data for the request (optional) # @param request_options [<%= gem_namespace %>::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) - super(base_url:, path:, method:, headers:, query:, request_options:) +<% if (requestLevelMaxRetries) { %> # @param max_retries [Integer, nil] Overrides the client's retry count for this request +<% } %> def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}<% if (requestLevelMaxRetries) { %>, max_retries: nil<% } %>) + super(base_url:, path:, method:, headers:, query:, request_options:<% if (requestLevelMaxRetries) { %>, max_retries:<% } %>) @body = body end diff --git a/generators/ruby-v2/base/src/asIs/internal/url_encoded/request.Template.rb b/generators/ruby-v2/base/src/asIs/internal/url_encoded/request.Template.rb index bd956971c227..53e86f65ef8b 100644 --- a/generators/ruby-v2/base/src/asIs/internal/url_encoded/request.Template.rb +++ b/generators/ruby-v2/base/src/asIs/internal/url_encoded/request.Template.rb @@ -14,8 +14,9 @@ class Request < <%= gem_namespace %>::Internal::Http::BaseRequest # @param query [Hash] Query parameters for the request (optional) # @param body [Object, nil] The form-urlencoded request body (optional) # @param request_options [<%= gem_namespace %>::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) - super(base_url:, path:, method:, headers:, query:, request_options:) +<% if (requestLevelMaxRetries) { %> # @param max_retries [Integer, nil] Overrides the client's retry count for this request +<% } %> def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}<% if (requestLevelMaxRetries) { %>, max_retries: nil<% } %>) + super(base_url:, path:, method:, headers:, query:, request_options:<% if (requestLevelMaxRetries) { %>, max_retries:<% } %>) @body = body end diff --git a/generators/ruby-v2/base/src/index.ts b/generators/ruby-v2/base/src/index.ts index 6c038a3902dd..ddee7d3fb135 100644 --- a/generators/ruby-v2/base/src/index.ts +++ b/generators/ruby-v2/base/src/index.ts @@ -3,3 +3,4 @@ export * from "./cli/index.js"; export * from "./context/AbstractRubyGeneratorContext.js"; export * from "./FileGenerator.js"; export * from "./project/index.js"; +export * from "./utils/retries.js"; diff --git a/generators/ruby-v2/base/src/project/RubyProject.ts b/generators/ruby-v2/base/src/project/RubyProject.ts index f32be6283a26..f07184ca3609 100644 --- a/generators/ruby-v2/base/src/project/RubyProject.ts +++ b/generators/ruby-v2/base/src/project/RubyProject.ts @@ -8,6 +8,7 @@ import { mkdir, readFile, writeFile } from "fs/promises"; import { join as pathJoin } from "path"; import { AsIsFiles, topologicalCompareAsIsFiles } from "../AsIs.js"; import { AbstractRubyGeneratorContext } from "../context/AbstractRubyGeneratorContext.js"; +import { hasEndpointWithRetriesDisabled } from "../utils/retries.js"; import { RubocopFile } from "./RubocopFile.js"; const eta = new Eta({ autoEscape: false, useWith: true, autoTrim: false }); @@ -213,7 +214,10 @@ export class RubyProject extends AbstractProject service.endpoints) + ) }) ); } @@ -230,7 +234,8 @@ export class RubyProject extends AbstractProject { let rendered = replaceTemplate({ contents: (await readFile(getAsIsFilepath(filename))).toString(), @@ -255,7 +261,8 @@ export class RubyProject extends AbstractProject { return { gem_namespace: gemNamespace, @@ -349,7 +358,10 @@ function getTemplateVariables({ respectOptionalRequestBody: respectOptionalRequestBody ?? false, // Emits the RawClient#auth_headers_for_endpoint delegator only for // endpoint-security SDKs, so ALL/ANY SDKs see zero change to raw_client.rb. - endpointSecurity: endpointSecurity ?? false + endpointSecurity: endpointSecurity ?? false, + // Emits the request-level `max_retries` override only for APIs with an endpoint + // that disables retries, so every other SDK's core files stay byte-identical. + requestLevelMaxRetries: requestLevelMaxRetries ?? false }; } diff --git a/generators/ruby-v2/base/src/utils/__test__/retries.test.ts b/generators/ruby-v2/base/src/utils/__test__/retries.test.ts new file mode 100644 index 000000000000..acc0edf8059d --- /dev/null +++ b/generators/ruby-v2/base/src/utils/__test__/retries.test.ts @@ -0,0 +1,24 @@ +import { areRetriesDisabled, hasEndpointWithRetriesDisabled } from "../retries.js"; + +describe("areRetriesDisabled", () => { + test("is true when the endpoint disables retries", () => { + expect(areRetriesDisabled({ retries: { disabled: true } })).toBe(true); + }); + + test("is false when retries are absent or not disabled", () => { + expect(areRetriesDisabled({ retries: undefined })).toBe(false); + expect(areRetriesDisabled({ retries: { disabled: false } })).toBe(false); + expect(areRetriesDisabled({ retries: { disabled: undefined } })).toBe(false); + }); +}); + +describe("hasEndpointWithRetriesDisabled", () => { + test("is true when any endpoint disables retries", () => { + expect(hasEndpointWithRetriesDisabled([{ retries: undefined }, { retries: { disabled: true } }])).toBe(true); + }); + + test("is false when no endpoint disables retries", () => { + expect(hasEndpointWithRetriesDisabled([{ retries: undefined }, { retries: { disabled: false } }])).toBe(false); + expect(hasEndpointWithRetriesDisabled([])).toBe(false); + }); +}); diff --git a/generators/ruby-v2/base/src/utils/retries.ts b/generators/ruby-v2/base/src/utils/retries.ts new file mode 100644 index 000000000000..b03c5846d06f --- /dev/null +++ b/generators/ruby-v2/base/src/utils/retries.ts @@ -0,0 +1,9 @@ +import { FernIr } from "@fern-fern/ir-sdk"; + +export function areRetriesDisabled(endpoint: Pick): boolean { + return endpoint.retries?.disabled === true; +} + +export function hasEndpointWithRetriesDisabled(endpoints: Pick[]): boolean { + return endpoints.some(areRetriesDisabled); +} diff --git a/generators/ruby-v2/sdk/changes/1.23.1/fix-oauth-token-endpoint-wire-test-stub.yml b/generators/ruby-v2/sdk/changes/1.23.1/fix-oauth-token-endpoint-wire-test-stub.yml new file mode 100644 index 000000000000..ecd974b6ae84 --- /dev/null +++ b/generators/ruby-v2/sdk/changes/1.23.1/fix-oauth-token-endpoint-wire-test-stub.yml @@ -0,0 +1,6 @@ +- summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix diff --git a/generators/ruby-v2/sdk/changes/1.23.2/respect-endpoint-level-disabled-retries.yml b/generators/ruby-v2/sdk/changes/1.23.2/respect-endpoint-level-disabled-retries.yml new file mode 100644 index 000000000000..31a486a5009f --- /dev/null +++ b/generators/ruby-v2/sdk/changes/1.23.2/respect-endpoint-level-disabled-retries.yml @@ -0,0 +1,6 @@ +- summary: | + Respect endpoint-level disabled retries. Endpoints configured with + `retries: { disabled: true }` (`x-fern-retries` in OpenAPI) now pin the + generated request's retry count to zero, so they are never retried + regardless of the client-level or per-request retry settings. + type: fix diff --git a/generators/ruby-v2/sdk/src/endpoint/http/RawClient.ts b/generators/ruby-v2/sdk/src/endpoint/http/RawClient.ts index 574994a7d6c4..f0cb63e3e273 100644 --- a/generators/ruby-v2/sdk/src/endpoint/http/RawClient.ts +++ b/generators/ruby-v2/sdk/src/endpoint/http/RawClient.ts @@ -1,4 +1,5 @@ import { ruby } from "@fern-api/ruby-ast"; +import { areRetriesDisabled } from "@fern-api/ruby-base"; import { FernIr } from "@fern-fern/ir-sdk"; import { SdkGeneratorContext } from "../../SdkGeneratorContext.js"; @@ -81,7 +82,9 @@ export class RawClient { if (omitContentTypeWithoutBody === true && requestType === "json") { writer.writeLine(`omit_content_type_without_body: true,`); } - writer.writeLine(`request_options: request_options`); + writer.write(`request_options: request_options`); + this.writeMaxRetriesOverride({ writer, endpoint }); + writer.writeLine(); writer.dedent(); writer.write(`)`); }); @@ -108,7 +111,9 @@ export class RawClient { if (bodyReference != null) { writer.writeLine(`body: ${bodyReference},`); } - writer.writeLine(`request_options: request_options`); + writer.write(`request_options: request_options`); + this.writeMaxRetriesOverride({ writer, endpoint }); + writer.writeLine(); writer.dedent(); writer.write(`)`); }); @@ -130,12 +135,30 @@ export class RawClient { if (queryBagReference != null) { writer.writeLine(`query: ${queryBagReference},`); } - writer.writeLine(`request_options: request_options`); + writer.write(`request_options: request_options`); + this.writeMaxRetriesOverride({ writer, endpoint }); + writer.writeLine(); writer.dedent(); writer.write(`)`); }); } + // Endpoints configured with `retries: { disabled: true }` pin the request's retry count to + // zero, which wins over both the client-level and the per-request retry settings. + private writeMaxRetriesOverride({ + writer, + endpoint + }: { + writer: ruby.Writer; + endpoint: FernIr.HttpEndpoint; + }): void { + if (!areRetriesDisabled(endpoint)) { + return; + } + writer.writeLine(","); + writer.write("max_retries: 0"); + } + private writePathString({ writer, endpoint, diff --git a/generators/ruby-v2/sdk/versions.yml b/generators/ruby-v2/sdk/versions.yml index 1c964dd53337..f2938fff29c4 100644 --- a/generators/ruby-v2/sdk/versions.yml +++ b/generators/ruby-v2/sdk/versions.yml @@ -1,4 +1,24 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 1.23.2 + changelogEntry: + - summary: | + Respect endpoint-level disabled retries. Endpoints configured with + `retries: { disabled: true }` (`x-fern-retries` in OpenAPI) now pin the + generated request's retry count to zero, so they are never retried + regardless of the client-level or per-request retry settings. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 1.23.1 + changelogEntry: + - summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 1.23.0 changelogEntry: - summary: | diff --git a/generators/rust/sdk/changes/0.46.1/fix-oauth-token-endpoint-wire-test-stub.yml b/generators/rust/sdk/changes/0.46.1/fix-oauth-token-endpoint-wire-test-stub.yml new file mode 100644 index 000000000000..ecd974b6ae84 --- /dev/null +++ b/generators/rust/sdk/changes/0.46.1/fix-oauth-token-endpoint-wire-test-stub.yml @@ -0,0 +1,6 @@ +- summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix diff --git a/generators/rust/sdk/changes/0.46.2/respect-endpoint-retries-disabled.yml b/generators/rust/sdk/changes/0.46.2/respect-endpoint-retries-disabled.yml new file mode 100644 index 000000000000..ecc0b6868d01 --- /dev/null +++ b/generators/rust/sdk/changes/0.46.2/respect-endpoint-retries-disabled.yml @@ -0,0 +1,5 @@ +- summary: | + Endpoints that declare `retries: { disabled: true }` (`x-fern-retries` in OpenAPI) no longer + retry. The generated method pins `max_retries` to zero on the request options, overriding both + the client-level default and any caller-supplied value. + type: fix diff --git a/generators/rust/sdk/src/__test__/RetriesConfiguration.test.ts b/generators/rust/sdk/src/__test__/RetriesConfiguration.test.ts new file mode 100644 index 000000000000..3e52e6edf091 --- /dev/null +++ b/generators/rust/sdk/src/__test__/RetriesConfiguration.test.ts @@ -0,0 +1,97 @@ +import { FernIr } from "@fern-fern/ir-sdk"; +import { HttpService, Subpackage } from "@fern-fern/ir-sdk/serialization"; +import { describe, expect, it } from "vitest"; +import { SubClientGenerator } from "../generators/SubClientGenerator.js"; +import { createSampleGeneratorContext } from "./util/createSampleGeneratorContext.js"; + +function nameJson(name: string): unknown { + return { + originalName: name, + camelCase: { unsafeName: name, safeName: name }, + snakeCase: { unsafeName: name.toLowerCase(), safeName: name.toLowerCase() }, + screamingSnakeCase: { unsafeName: name.toUpperCase(), safeName: name.toUpperCase() }, + pascalCase: { unsafeName: name, safeName: name } + }; +} + +// Unrecognized keys are passed through so the fixtures stay valid across IR versions. +const PARSE_OPTS = { unrecognizedObjectKeys: "passthrough" } as const; + +function createService(retries: { disabled: boolean } | undefined): FernIr.HttpService { + return HttpService.parseOrThrow( + { + name: { fernFilepath: { allParts: [], packagePath: [], file: undefined } }, + basePath: { head: "/users", parts: [] }, + headers: [], + pathParameters: [], + endpoints: [ + { + id: "getUsers", + name: nameJson("getUsers"), + method: "GET", + headers: [], + path: { head: "", parts: [] }, + fullPath: { head: "/users", parts: [] }, + pathParameters: [], + allPathParameters: [], + queryParameters: [], + errors: [], + auth: false, + idempotent: false, + userSpecifiedExamples: [], + autogeneratedExamples: [], + retries + } + ] + }, + PARSE_OPTS + ); +} + +function createSubpackage(serviceId: string): FernIr.Subpackage { + return Subpackage.parseOrThrow( + { + name: nameJson("users"), + fernFilepath: { allParts: [], packagePath: [], file: undefined }, + service: serviceId, + types: [], + errors: [], + subpackages: [], + hasEndpointsInTree: true, + hasWebSocketInTree: false + }, + PARSE_OPTS + ); +} + +function generateClient(retries: { disabled: boolean } | undefined): string { + const service = createService(retries); + const context = createSampleGeneratorContext(); + context.ir.headers = []; + context.getHttpServiceOrThrow = () => service; + context.getSubpackagesOrThrow = () => []; + context.getUniqueClientNameForSubpackage = () => "UsersClient"; + context.getEndpointSnippet = () => undefined; + + const generator = new SubClientGenerator(context, createSubpackage("service_users")); + return generator.generateRawClientContent().rawDeclarations.join("\n"); +} + +describe("retries configuration", () => { + it("pins max_retries to zero when the endpoint disables retries", () => { + const client = generateClient({ disabled: true }); + expect(client).toContain("let mut o = options.unwrap_or_default();"); + expect(client).toContain("o.max_retries = Some(0);"); + }); + + it("leaves options untouched when the endpoint does not configure retries", () => { + const client = generateClient(undefined); + expect(client).not.toContain("max_retries"); + expect(client).not.toContain("options.unwrap_or_default()"); + }); + + it("leaves options untouched when retries are explicitly enabled", () => { + const client = generateClient({ disabled: false }); + expect(client).not.toContain("max_retries"); + }); +}); diff --git a/generators/rust/sdk/src/generators/SubClientGenerator.ts b/generators/rust/sdk/src/generators/SubClientGenerator.ts index 435b7b80f0c3..f1d72d3036ce 100644 --- a/generators/rust/sdk/src/generators/SubClientGenerator.ts +++ b/generators/rust/sdk/src/generators/SubClientGenerator.ts @@ -952,7 +952,7 @@ export class SubClientGenerator { // Only apply to execute_request and execute_stream_request (we have _with_base_url variants for these). const urlMethodName = this.getEndpointUrlMethodName(endpoint); const supportsBaseUrlOverride = executeMethod === "execute_request" || executeMethod === "execute_stream_request"; - const literalHeadersPrelude = this.buildLiteralHeadersPrelude(endpoint); + const requestOptionsPrelude = this.buildRequestOptionsPrelude(endpoint); let body: string; if (urlMethodName && supportsBaseUrlOverride) { @@ -960,11 +960,11 @@ export class SubClientGenerator { const baseUrlResolution = `let base_url = self.http_client.config().environment.as_ref()\n` + ` .map_or(self.http_client.base_url(), |env| env.${urlMethodName}());\n`; - body = `${literalHeadersPrelude}${baseUrlResolution} self.http_client.${executeMethod}_with_base_url${typeParameter}( + body = `${requestOptionsPrelude}${baseUrlResolution} self.http_client.${executeMethod}_with_base_url${typeParameter}( base_url,${executeArgs} ).await`; } else { - body = `${literalHeadersPrelude}self.http_client.${executeMethod}${typeParameter}(${executeArgs} + body = `${requestOptionsPrelude}self.http_client.${executeMethod}${typeParameter}(${executeArgs} ).await`; } @@ -1005,9 +1005,11 @@ export class SubClientGenerator { * (e.g. `Accept-Encoding: literal<"gzip">`) into the request options, and — in * endpoint-security mode — the auth headers routed for this endpoint's declared * `security` requirements. Caller-supplied literal headers take precedence over the - * literal defaults; routed auth headers are authoritative. + * literal defaults; routed auth headers are authoritative. Endpoints that declare + * `retries: { disabled: true }` also pin `max_retries` to zero, overriding both the + * client default and any caller-supplied value. */ - private buildLiteralHeadersPrelude(endpoint: FernIr.HttpEndpoint): string { + private buildRequestOptionsPrelude(endpoint: FernIr.HttpEndpoint): string { const literalHeaders: { wireValue: string; value: string }[] = []; for (const header of [...(this.context.ir.headers ?? []), ...(this.service?.headers ?? []), ...(endpoint.headers ?? [])]) { const value = this.getLiteralHeaderValue(header.valueType); @@ -1017,7 +1019,8 @@ export class SubClientGenerator { } const authRequirements = this.getEndpointSecurityRequirements(endpoint); const needsAuthRouting = authRequirements != null && authRequirements.length > 0; - if (literalHeaders.length === 0 && !needsAuthRouting) { + const retriesDisabled = endpoint.retries?.disabled === true; + if (literalHeaders.length === 0 && !needsAuthRouting && !retriesDisabled) { return ""; } const literalInserts = literalHeaders @@ -1050,6 +1053,9 @@ export class SubClientGenerator { ` o.additional_headers.insert(header_key, header_value);\n` + ` }\n`; } + if (retriesDisabled) { + prelude += ` o.max_retries = Some(0);\n`; + } prelude += ` Some(o)\n` + ` };\n `; return prelude; } diff --git a/generators/rust/sdk/versions.yml b/generators/rust/sdk/versions.yml index 3bb3a0cf35bc..9e5c7af25dd1 100644 --- a/generators/rust/sdk/versions.yml +++ b/generators/rust/sdk/versions.yml @@ -1,4 +1,23 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 0.46.2 + changelogEntry: + - summary: | + Endpoints that declare `retries: { disabled: true }` (`x-fern-retries` in OpenAPI) no longer + retry. The generated method pins `max_retries` to zero on the request options, overriding both + the client-level default and any caller-supplied value. + type: fix + createdAt: "2026-08-20" + irVersion: 67 +- version: 0.46.1 + changelogEntry: + - summary: | + Fixed generated WireMock stubs for OAuth client credentials APIs whose token endpoint is + itself marked as authenticated: the token (and refresh) endpoint stub no longer requires + an `Authorization` header, since the token request is made to obtain the token and cannot + carry one yet. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 0.46.0 changelogEntry: - summary: | diff --git a/generators/swift/base/src/Template.ts b/generators/swift/base/src/Template.ts index dabe85f0dff3..eea47b6504a5 100644 --- a/generators/swift/base/src/Template.ts +++ b/generators/swift/base/src/Template.ts @@ -46,6 +46,10 @@ const TestTemplateFileSpecs = { relativePath: "Core", filenameWithoutExtension: () => "ClientRetryTests" }, + ClientRetriesDisabledTests: { + relativePath: "Core", + filenameWithoutExtension: () => "ClientRetriesDisabledTests" + }, // Utilities HTTPStub: { relativePath: "Utilities", diff --git a/generators/swift/base/src/template/Sources/HTTPClient.Template.swift b/generators/swift/base/src/template/Sources/HTTPClient.Template.swift index f0167b91ae22..e6abb1130c3e 100644 --- a/generators/swift/base/src/template/Sources/HTTPClient.Template.swift +++ b/generators/swift/base/src/template/Sources/HTTPClient.Template.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/generators/swift/base/src/template/Tests/ClientRetriesDisabledTests.Template.swift b/generators/swift/base/src/template/Tests/ClientRetriesDisabledTests.Template.swift new file mode 100644 index 000000000000..c87ed2093cfa --- /dev/null +++ b/generators/swift/base/src/template/Tests/ClientRetriesDisabledTests.Template.swift @@ -0,0 +1,41 @@ +import <%= moduleName %> +import Foundation +import Testing + +@Suite("Client Retries Disabled Tests") struct ClientRetriesDisabledTests { + @Test func testDoesNotRetryOn500InternalServerError() async throws { + let stub = HTTPStub() + stub.setResponseSequence([ + (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()), + ( + statusCode: 200, headers: ["Content-Type": "application/json"], + body: Foundation.Data("true".utf8) + ), + ]) + +<%= clientDeclaration %> + do { +<%= endpointCall %> + } catch { + } + try #require(stub.getRequestCount() == 1) + } + + @Test func testIgnoresRequestLevelMaxRetries() async throws { + let stub = HTTPStub() + stub.setResponseSequence([ + (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()), + ( + statusCode: 200, headers: ["Content-Type": "application/json"], + body: Foundation.Data("true".utf8) + ), + ]) + +<%= clientDeclaration %> + do { +<%= endpointCallMaxRetries5 %> + } catch { + } + try #require(stub.getRequestCount() == 1) + } +} diff --git a/generators/swift/sdk/changes/0.36.1/honor-retries-disabled.yml b/generators/swift/sdk/changes/0.36.1/honor-retries-disabled.yml new file mode 100644 index 000000000000..bab41e11dd7c --- /dev/null +++ b/generators/swift/sdk/changes/0.36.1/honor-retries-disabled.yml @@ -0,0 +1,6 @@ +- summary: | + Endpoints configured with `x-fern-retries: { disabled: true }` (or `retries: { disabled: true }` in a Fern + definition) are no longer retried. The generated endpoint method passes `retriesDisabled: true` to the + HTTP client, which takes precedence over both the client-level `maxRetries` and the per-request + `RequestOptions.maxRetries`. + type: fix diff --git a/generators/swift/sdk/src/__test__/HTTPClientTemplate.test.ts b/generators/swift/sdk/src/__test__/HTTPClientTemplate.test.ts new file mode 100644 index 000000000000..5571ad890ac5 --- /dev/null +++ b/generators/swift/sdk/src/__test__/HTTPClientTemplate.test.ts @@ -0,0 +1,9 @@ +import { SourceTemplateFiles } from "@fern-api/swift-base"; +import { describe, expect, it } from "vitest"; + +describe("HTTPClient template", () => { + it("matches snapshot", async () => { + const contents = await SourceTemplateFiles.HTTPClient.loadContents(); + expect(contents).toMatchSnapshot(); + }); +}); diff --git a/generators/swift/sdk/src/__test__/__snapshots__/HTTPClientTemplate.test.ts.snap b/generators/swift/sdk/src/__test__/__snapshots__/HTTPClientTemplate.test.ts.snap new file mode 100644 index 000000000000..4847d799a4e5 --- /dev/null +++ b/generators/swift/sdk/src/__test__/__snapshots__/HTTPClientTemplate.test.ts.snap @@ -0,0 +1,409 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`HTTPClient template > matches snapshot 1`] = ` +"import Foundation + +final class HTTPClient: Swift.Sendable { + private let clientConfig: ClientConfig + private let jsonEncoder = Serde.jsonEncoder + private let jsonDecoder = Serde.jsonDecoder + + private static let initialRetryDelay: Foundation.TimeInterval = 1.0 // 1 second + private static let maxRetryDelay: Foundation.TimeInterval = 60.0 // 60 seconds + private static let jitterFactor: Swift.Double = 0.2 // 20% jitter + + init(config: ClientConfig) { + self.clientConfig = config + } + + /// Performs a request with no response. + func performRequest( + method: HTTP.Method, + path: Swift.String, + contentType requestContentType: HTTP.ContentType = .applicationJson, + headers requestHeaders: [Swift.String: Swift.String?] = [:], + queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], + body requestBody: Any? = nil, + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false + ) async throws { + _ = try await performRequest( + method: method, + path: path, + contentType: requestContentType, + headers: requestHeaders, + queryParams: requestQueryParams, + body: requestBody, + requestOptions: requestOptions, + retriesDisabled: retriesDisabled, + responseType: Foundation.Data.self + ) + } + + /// Performs a request with the specified response type. + func performRequest( + method: HTTP.Method, + path: Swift.String, + contentType requestContentType: HTTP.ContentType = .applicationJson, + headers requestHeaders: [Swift.String: Swift.String?] = [:], + queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], + body requestBody: Any? = nil, + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, + responseType: T.Type + ) async throws -> T { + let requestBody: HTTP.RequestBody? = requestBody.map { body in + if let multipartData = body as? MultipartFormData { + return .multipartFormData(multipartData) + } else if let data = body as? Foundation.Data { + return .data(data) + } else if let encodable = body as? any Swift.Encodable { + return .jsonEncodable(encodable) + } else { + preconditionFailure("Unsupported body type: \\(type(of: body))") + } + } + + let request = try await buildRequest( + method: method, + path: path, + requestContentType: requestContentType, + requestHeaders: requestHeaders, + requestQueryParams: requestQueryParams, + requestBody: requestBody, + requestOptions: requestOptions + ) + + let (data, _) = try await executeRequestWithURLSession( + request, + requestOptions: requestOptions, + retriesDisabled: retriesDisabled + ) + + if responseType == Foundation.Data.self { + if let data = data as? T { + return data + } else { + throw <%= errorEnumName %>.invalidResponse + } + } + + if responseType == Swift.String.self { + if let string = Swift.String(data: data, encoding: .utf8) as? T { + return string + } else { + throw <%= errorEnumName %>.invalidResponse + } + } + + do { + return try jsonDecoder.decode(responseType, from: data) + } catch { + throw <%= errorEnumName %>.decodingError(error) + } + } + + private func buildRequest( + method: HTTP.Method, + path: Swift.String, + requestContentType: HTTP.ContentType, + requestHeaders: [Swift.String: Swift.String?], + requestQueryParams: [Swift.String: QueryParameter?], + requestBody: HTTP.RequestBody? = nil, + requestOptions: RequestOptions? = nil + ) async throws -> Networking.URLRequest { + // Init with URL + let url = buildRequestURL( + path: path, requestQueryParams: requestQueryParams, requestOptions: requestOptions + ) + var request = Networking.URLRequest(url: url) + + // Set timeout + if let timeout = requestOptions?.timeout { + request.timeoutInterval = Foundation.TimeInterval(timeout) + } + + // Set method + request.httpMethod = method.rawValue + + // Set headers + let headers = try await buildRequestHeaders( + requestBody: requestBody, + requestContentType: requestContentType, + requestHeaders: requestHeaders, + requestOptions: requestOptions + ) + for (key, value) in headers { + request.setValue(value, forHTTPHeaderField: key) + } + + // Set body + if let requestBody = requestBody { + request.httpBody = buildRequestBody( + requestBody: requestBody, + requestOptions: requestOptions + ) + } + + return request + } + + private func buildRequestURL( + path: Swift.String, + requestQueryParams: [Swift.String: QueryParameter?], + requestOptions: RequestOptions? = nil + ) -> URL { + let endpointURL = "\\(clientConfig.baseURL)\\(path)" + guard var components = Foundation.URLComponents(string: endpointURL) else { + preconditionFailure( + "Invalid URL '\\(endpointURL)' - this indicates an unexpected error in the SDK." + ) + } + if !requestQueryParams.isEmpty { + let baseItems: [Foundation.URLQueryItem] = requestQueryParams.compactMap { key, value in + guard let unwrapped = value else { return nil } + let stringValue = unwrapped.toString() + guard !stringValue.isEmpty else { return nil } + return Foundation.URLQueryItem(name: key, value: stringValue) + } + if !baseItems.isEmpty { + components.queryItems = baseItems + } + } + if let additionalQueryParams = requestOptions?.additionalQueryParameters { + let extraItems = additionalQueryParams.compactMap { key, value in + value.isEmpty ? nil : Foundation.URLQueryItem(name: key, value: value) + } + if components.queryItems == nil { + components.queryItems = extraItems + } else { + components.queryItems?.append(contentsOf: extraItems) + } + } + guard let url = components.url else { + preconditionFailure( + "Failed to construct URL from components - this indicates an unexpected error in the SDK." + ) + } + return url + } + + private func buildRequestHeaders( + requestBody: HTTP.RequestBody?, + requestContentType: HTTP.ContentType, + requestHeaders: [Swift.String: Swift.String?], + requestOptions: RequestOptions? = nil + ) async throws -> [Swift.String: Swift.String] { + var headers = clientConfig.headers ?? [:] + + headers["Content-Type"] = buildContentTypeHeader( + requestBody: requestBody, + requestContentType: requestContentType + ) + + if let headerAuth = clientConfig.headerAuth { + headers[headerAuth.header] = requestOptions?.apiKey ?? headerAuth.key + } + if let basicAuthToken = clientConfig.basicAuth?.token { + headers["Authorization"] = "Basic \\(basicAuthToken)" + } + if let bearerAuthToken = try await getBearerAuthToken(requestOptions) { + headers["Authorization"] = "Bearer \\(bearerAuthToken)" + } + for (key, value) in requestHeaders { + if let value = value { + headers[key] = value + } + } + for (key, value) in requestOptions?.additionalHeaders ?? [:] { + headers[key] = value + } + + return headers + } + + private func buildContentTypeHeader( + requestBody: HTTP.RequestBody?, + requestContentType: HTTP.ContentType, + ) -> Swift.String { + var contentType = requestContentType.rawValue + if let requestBody, case .multipartFormData(let multipartData) = requestBody { + if contentType != HTTP.ContentType.multipartFormData.rawValue { + preconditionFailure( + "The content type for multipart form data requests must be multipart/form-data - this indicates an unexpected error in the SDK." + ) + } + // Multipart form data content type must include the boundary + contentType = "\\(contentType); boundary=\\(multipartData.boundary)" + } + return contentType + } + + private func getBearerAuthToken(_ requestOptions: RequestOptions?) async throws -> Swift.String? + { + if let tokenString = requestOptions?.token { + return tokenString + } + if let bearerAuth = clientConfig.bearerAuth { + return try await bearerAuth.token.retrieve() + } + return nil + } + + private func buildRequestBody( + requestBody: HTTP.RequestBody, + requestOptions: RequestOptions? = nil + ) -> Foundation.Data { + switch requestBody { + case .jsonEncodable(let encodableBody): + do { + return try jsonEncoder.encode(encodableBody) + } catch { + preconditionFailure( + "Failed to encode request body: \\(error) - this indicates an unexpected error in the SDK." + ) + } + case .data(let dataBody): + return dataBody + case .multipartFormData(let multipartData): + return multipartData.data() + } + } + + private func executeRequestWithURLSession( + _ request: Networking.URLRequest, + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false + ) async throws -> (Foundation.Data, Swift.String?) { + // Endpoints declaring \`retries: { disabled: true }\` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) + var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? + + for attempt in 0...maxRetries { + do { + let (data, response) = try await clientConfig.urlSession.data(for: request) + + guard let httpResponse = response as? Networking.HTTPURLResponse else { + throw <%= errorEnumName %>.invalidResponse + } + + // Handle successful responses + if 200...299 ~= httpResponse.statusCode { + let contentType = httpResponse.value(forHTTPHeaderField: "Content-Type") + return (data, contentType) + } + + lastResponse = (data, httpResponse) + + if attempt < maxRetries && shouldRetry(statusCode: httpResponse.statusCode) { + let delay = getRetryDelay(response: httpResponse, retryAttempt: attempt) + try await _Concurrency.Task.sleep( + nanoseconds: Swift.UInt64(delay * 1_000_000_000)) + continue + } + + throw makeErrorFromResponse( + statusCode: httpResponse.statusCode, + data: data + ) + } catch { + let clientError: <%= errorEnumName %>? + + // Treat timeouts as a first-class, non-retryable error + if let urlError = error as? Foundation.URLError, urlError.code == .timedOut { + clientError = .timeout(error) + } else if let existingClientError = error as? <%= errorEnumName %> { + clientError = existingClientError + } else { + clientError = nil + } + + if attempt >= maxRetries || clientError != nil { + if let clientError { + throw clientError + } else { + throw <%= errorEnumName %>.networkError(error) + } + } + let delay = Self.initialRetryDelay * pow(2.0, Swift.Double(attempt)) + let cappedDelay = min(delay, Self.maxRetryDelay) + let jitteredDelay = addSymmetricJitter(to: cappedDelay) + try await _Concurrency.Task.sleep( + nanoseconds: Swift.UInt64(jitteredDelay * 1_000_000_000)) + } + } + + if let (data, httpResponse) = lastResponse { + throw makeErrorFromResponse(statusCode: httpResponse.statusCode, data: data) + } + throw <%= errorEnumName %>.invalidResponse + } + + private func shouldRetry(statusCode: Swift.Int) -> Swift.Bool { + return {{RETRY_STATUS_CHECK}} + } + + private func getRetryDelay(response: Networking.HTTPURLResponse, retryAttempt: Swift.Int) + -> Foundation.TimeInterval + { + if let retryAfter = response.value(forHTTPHeaderField: "Retry-After") { + if let seconds = Swift.Double(retryAfter), seconds > 0 { + return min(seconds, Self.maxRetryDelay) + } + + if let date = parseHTTPDate(retryAfter) { + let delay = date.timeIntervalSinceNow + if delay > 0 { + return min(delay, Self.maxRetryDelay) + } + } + } + + if let rateLimitReset = response.value(forHTTPHeaderField: "X-RateLimit-Reset") { + if let resetTimeSeconds = Swift.Double(rateLimitReset) { + let resetDate = Foundation.Date(timeIntervalSince1970: resetTimeSeconds) + let delay = resetDate.timeIntervalSinceNow + if delay > 0 { + let cappedDelay = min(delay, Self.maxRetryDelay) + return addPositiveJitter(to: cappedDelay) + } + } + } + + let baseDelay = Self.initialRetryDelay * pow(2.0, Swift.Double(retryAttempt)) + let cappedDelay = min(baseDelay, Self.maxRetryDelay) + return addSymmetricJitter(to: cappedDelay) + } + + private func parseHTTPDate(_ dateString: Swift.String) -> Foundation.Date? { + let formatter = Foundation.DateFormatter() + formatter.locale = Foundation.Locale(identifier: "en_US_POSIX") + formatter.timeZone = Foundation.TimeZone(abbreviation: "GMT") + formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz" + return formatter.date(from: dateString) + } + + private func addPositiveJitter(to delay: Foundation.TimeInterval) -> Foundation.TimeInterval { + let jitterMultiplier = 1.0 + Swift.Double.random(in: 0...Self.jitterFactor) + return delay * jitterMultiplier + } + + private func addSymmetricJitter(to delay: Foundation.TimeInterval) -> Foundation.TimeInterval { + let jitterMultiplier = + 1.0 + Swift.Double.random(in: -Self.jitterFactor / 2...Self.jitterFactor / 2) + return delay * jitterMultiplier + } + + private func makeErrorFromResponse(statusCode: Swift.Int, data: Foundation.Data) -> <%= errorEnumName %> + { + let httpError = HTTPError.from( + statusCode: statusCode, + data: data, + jsonDecoder: jsonDecoder + ) + return <%= errorEnumName %>.httpError(httpError) + } +} +" +`; diff --git a/generators/swift/sdk/src/generators/client/EndpointMethodGenerator.ts b/generators/swift/sdk/src/generators/client/EndpointMethodGenerator.ts index f85bda66832f..a088d2beb3ec 100644 --- a/generators/swift/sdk/src/generators/client/EndpointMethodGenerator.ts +++ b/generators/swift/sdk/src/generators/client/EndpointMethodGenerator.ts @@ -4,6 +4,7 @@ import { Referencer, swift } from "@fern-api/swift-codegen"; import { FernIr } from "@fern-fern/ir-sdk"; import { SdkGeneratorContext } from "../../SdkGeneratorContext.js"; import { ClientGeneratorContext } from "./ClientGeneratorContext.js"; +import { areRetriesDisabled } from "./util/are-retries-disabled.js"; import { formatEndpointPathForSwift } from "./util/format-endpoint-path-for-swift.js"; import { parseEndpointPath } from "./util/parse-endpoint-path.js"; @@ -417,6 +418,15 @@ export class EndpointMethodGenerator { }) ); + if (areRetriesDisabled(endpoint.retries)) { + arguments_.push( + swift.functionArgument({ + label: "retriesDisabled", + value: swift.Expression.rawValue("true") + }) + ); + } + const returnType = this.getMethodReturnTypeForEndpoint(endpoint); if (!this.referencer.resolvesToTheSwiftType(returnType, "Void")) { diff --git a/generators/swift/sdk/src/generators/client/util/__test__/are-retries-disabled.test.ts b/generators/swift/sdk/src/generators/client/util/__test__/are-retries-disabled.test.ts new file mode 100644 index 000000000000..ef3dee96cf1e --- /dev/null +++ b/generators/swift/sdk/src/generators/client/util/__test__/are-retries-disabled.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest"; +import { areRetriesDisabled } from "../are-retries-disabled.js"; + +describe("areRetriesDisabled", () => { + it("returns false when the endpoint has no retries configuration", () => { + expect(areRetriesDisabled(undefined)).toBe(false); + }); + + it("returns false when disabled is not set", () => { + expect(areRetriesDisabled({ disabled: undefined })).toBe(false); + }); + + it("returns false when disabled is false", () => { + expect(areRetriesDisabled({ disabled: false })).toBe(false); + }); + + it("returns true when disabled is true", () => { + expect(areRetriesDisabled({ disabled: true })).toBe(true); + }); +}); diff --git a/generators/swift/sdk/src/generators/client/util/are-retries-disabled.ts b/generators/swift/sdk/src/generators/client/util/are-retries-disabled.ts new file mode 100644 index 000000000000..c29329cd7b7c --- /dev/null +++ b/generators/swift/sdk/src/generators/client/util/are-retries-disabled.ts @@ -0,0 +1,5 @@ +import { FernIr } from "@fern-fern/ir-sdk"; + +export function areRetriesDisabled(retries: FernIr.RetriesConfiguration | undefined): boolean { + return retries?.disabled === true; +} diff --git a/generators/swift/sdk/src/generators/test/TemplateDataGenerator.ts b/generators/swift/sdk/src/generators/test/TemplateDataGenerator.ts index be0fc920f32b..ae7ce64c8cbd 100644 --- a/generators/swift/sdk/src/generators/test/TemplateDataGenerator.ts +++ b/generators/swift/sdk/src/generators/test/TemplateDataGenerator.ts @@ -4,7 +4,18 @@ import { swift } from "@fern-api/swift-codegen"; import { DynamicSnippetsGenerator, EndpointSnippetGenerator } from "@fern-api/swift-dynamic-snippets"; import { FernIr } from "@fern-fern/ir-sdk"; import { SdkGeneratorContext } from "../../SdkGeneratorContext.js"; -import { convertDynamicEndpointSnippetRequest } from "../../utils/convertEndpointSnippetRequest.js"; +import { + convertDynamicEndpointSnippetRequest, + EndpointSnippetRequest +} from "../../utils/convertEndpointSnippetRequest.js"; +import { areRetriesDisabled } from "../client/util/are-retries-disabled.js"; + +interface SampleEndpoint { + endpoint: FernIr.HttpEndpoint; + dynamicEndpoint: FernIr.dynamic.Endpoint; + dynamicEndpointExample: FernIr.dynamic.EndpointExample; + endpointSnippetRequest: EndpointSnippetRequest; +} export declare namespace TemplateDataGenerator { interface Args { @@ -74,6 +85,8 @@ export class TemplateDataGenerator { return this.generateTemplateDataForClientErrorTests(); case "ClientRetryTests": return this.generateTemplateDataForClientRetryTests(); + case "ClientRetriesDisabledTests": + return this.generateTemplateDataForClientRetriesDisabledTests(); case "HTTPStub": return this.generateTemplateDataForHTTPStub(); default: @@ -84,11 +97,12 @@ export class TemplateDataGenerator { private generateTemplateDataForClientErrorTests() { const moduleSymbol = this.context.project.nameRegistry.getRegisteredSourceModuleSymbolOrThrow(); const errorEnumSymbol = this.context.project.nameRegistry.getErrorEnumSymbolOrThrow(); - const clientDeclaration = this.generateRootClientInitializationStatement(); - const endpointCallExpression = this.generateEndpointMethodCallExpression(); - if (!clientDeclaration || !endpointCallExpression) { + const sampleEndpoint = this.getSampleEndpoint(this.getEndpointForClientErrorTests()); + if (!sampleEndpoint) { return null; } + const clientDeclaration = this.generateRootClientInitializationStatement(sampleEndpoint); + const endpointCallExpression = this.generateEndpointMethodCallExpression(sampleEndpoint); return { moduleName: moduleSymbol.name, errorEnumName: errorEnumSymbol.name, @@ -99,14 +113,13 @@ export class TemplateDataGenerator { private generateTemplateDataForClientRetryTests() { const moduleSymbol = this.context.project.nameRegistry.getRegisteredSourceModuleSymbolOrThrow(); - const sampleEndpoint = this.getSampleEndpoint(); - const clientDeclaration = this.generateRootClientInitializationStatement(); - const endpointCallExpression = this.generateEndpointMethodCallExpression(); - if (!sampleEndpoint || !clientDeclaration || !endpointCallExpression) { + const sampleEndpoint = this.getSampleEndpoint(this.getEndpointForClientRetryTests()); + if (!sampleEndpoint) { return null; } + const clientDeclaration = this.generateRootClientInitializationStatement(sampleEndpoint); + const endpointCallExpression = this.generateEndpointMethodCallExpression(sampleEndpoint); const defaultMaxRetries = this.context.customConfig.maxRetries ?? 2; - const { dynamicEndpoint, dynamicEndpointExample } = sampleEndpoint; return { moduleName: moduleSymbol.name, defaultMaxRetries, @@ -120,58 +133,57 @@ export class TemplateDataGenerator { endpointCallMaxRetriesExhausted: swift.Statement.discardAssignment(endpointCallExpression).toStringWithIndentation(4), endpointCallMaxRetries5: swift.Statement.discardAssignment( - this.endpointSnippetGenerator.generateEndpointMethodCallExpression({ - endpoint: dynamicEndpoint, - snippet: convertDynamicEndpointSnippetRequest(dynamicEndpointExample), - additionalArguments: [ - swift.functionArgument({ - label: "requestOptions", - value: swift.Expression.structInitialization({ - unsafeName: "RequestOptions", - arguments_: [ - swift.functionArgument({ - label: "maxRetries", - value: swift.Expression.numberLiteral(5) - }), - swift.functionArgument({ - label: "additionalHeaders", - value: swift.Expression.memberAccess({ - target: swift.Expression.reference("stub"), - memberName: "headers" - }) - }) - ] - }) - }) - ] - }) + this.generateEndpointMethodCallExpressionWithMaxRetries(sampleEndpoint, 5) ).toStringWithIndentation(4), endpointCallMaxRetriesZero: swift.Statement.discardAssignment( - this.endpointSnippetGenerator.generateEndpointMethodCallExpression({ - endpoint: dynamicEndpoint, - snippet: convertDynamicEndpointSnippetRequest(dynamicEndpointExample), - additionalArguments: [ - swift.functionArgument({ - label: "requestOptions", - value: swift.Expression.structInitialization({ - unsafeName: "RequestOptions", - arguments_: [ - swift.functionArgument({ - label: "maxRetries", - value: swift.Expression.numberLiteral(0) - }), - swift.functionArgument({ - label: "additionalHeaders", - value: swift.Expression.memberAccess({ - target: swift.Expression.reference("stub"), - memberName: "headers" - }) - }) - ] + this.generateEndpointMethodCallExpressionWithMaxRetries(sampleEndpoint, 0) + ).toStringWithIndentation(4) + }; + } + + private generateEndpointMethodCallExpressionWithMaxRetries(sampleEndpoint: SampleEndpoint, maxRetries: number) { + const { dynamicEndpoint, dynamicEndpointExample } = sampleEndpoint; + return this.endpointSnippetGenerator.generateEndpointMethodCallExpression({ + endpoint: dynamicEndpoint, + snippet: convertDynamicEndpointSnippetRequest(dynamicEndpointExample), + additionalArguments: [ + swift.functionArgument({ + label: "requestOptions", + value: swift.Expression.structInitialization({ + unsafeName: "RequestOptions", + arguments_: [ + swift.functionArgument({ + label: "maxRetries", + value: swift.Expression.numberLiteral(maxRetries) + }), + swift.functionArgument({ + label: "additionalHeaders", + value: swift.Expression.memberAccess({ + target: swift.Expression.reference("stub"), + memberName: "headers" + }) }) - }) - ] + ] + }) }) + ] + }); + } + + private generateTemplateDataForClientRetriesDisabledTests() { + const moduleSymbol = this.context.project.nameRegistry.getRegisteredSourceModuleSymbolOrThrow(); + const sampleEndpoint = this.getSampleEndpoint(this.getEndpointForClientRetriesDisabledTests()); + if (!sampleEndpoint) { + return null; + } + const clientDeclaration = this.generateRootClientInitializationStatement(sampleEndpoint); + const endpointCallExpression = this.generateEndpointMethodCallExpression(sampleEndpoint); + return { + moduleName: moduleSymbol.name, + clientDeclaration: clientDeclaration.toStringWithIndentation(3), + endpointCall: swift.Statement.discardAssignment(endpointCallExpression).toStringWithIndentation(4), + endpointCallMaxRetries5: swift.Statement.discardAssignment( + this.generateEndpointMethodCallExpressionWithMaxRetries(sampleEndpoint, 5) ).toStringWithIndentation(4) }; } @@ -194,11 +206,7 @@ export class TemplateDataGenerator { }; } - private generateRootClientInitializationStatement() { - const sampleEndpoint = this.getSampleEndpoint(); - if (!sampleEndpoint) { - return null; - } + private generateRootClientInitializationStatement(sampleEndpoint: SampleEndpoint) { const { dynamicEndpoint, endpointSnippetRequest } = sampleEndpoint; return this.endpointSnippetGenerator.generateRootClientInitializationStatement({ auth: dynamicEndpoint.auth, @@ -215,11 +223,7 @@ export class TemplateDataGenerator { }); } - private generateEndpointMethodCallExpression() { - const sampleEndpoint = this.getSampleEndpoint(); - if (!sampleEndpoint) { - return null; - } + private generateEndpointMethodCallExpression(sampleEndpoint: SampleEndpoint) { const { dynamicEndpoint, dynamicEndpointExample } = sampleEndpoint; return this.endpointSnippetGenerator.generateEndpointMethodCallExpression({ endpoint: dynamicEndpoint, @@ -244,8 +248,7 @@ export class TemplateDataGenerator { }); } - private getSampleEndpoint() { - const endpoint = this.getEndpointForClientRetryTests(); + private getSampleEndpoint(endpoint: FernIr.HttpEndpoint | undefined): SampleEndpoint | null { if (!endpoint) { return null; } @@ -265,7 +268,7 @@ export class TemplateDataGenerator { }; } - private getEndpointForClientRetryTests() { + private getEndpointForClientErrorTests() { const { services } = this.context.ir; for (const serviceId in services) { const service = services[serviceId]; @@ -274,6 +277,38 @@ export class TemplateDataGenerator { return undefined; } + /** + * The retry test suite asserts that requests are retried, so it can only be generated for an + * endpoint that has retries enabled. + */ + private getEndpointForClientRetryTests() { + const { services } = this.context.ir; + for (const serviceId in services) { + const service = services[serviceId]; + const endpoint = service?.endpoints.find((endpoint) => !areRetriesDisabled(endpoint.retries)); + if (endpoint) { + return endpoint; + } + } + return undefined; + } + + /** + * The retries-disabled test suite asserts that requests are not retried, so it can only be generated + * for an endpoint that has retries disabled. + */ + private getEndpointForClientRetriesDisabledTests() { + const { services } = this.context.ir; + for (const serviceId in services) { + const service = services[serviceId]; + const endpoint = service?.endpoints.find((endpoint) => areRetriesDisabled(endpoint.retries)); + if (endpoint) { + return endpoint; + } + } + return undefined; + } + private getDynamicEndpointForEndpoint(endpoint: FernIr.HttpEndpoint) { const dynamicEndpoint = this.dynamicIr.endpoints[endpoint.id]; assertDefined(dynamicEndpoint, "Dynamic endpoint is required to generate wire tests."); diff --git a/generators/swift/sdk/versions.yml b/generators/swift/sdk/versions.yml index 7fb63684f67b..4afc81878234 100644 --- a/generators/swift/sdk/versions.yml +++ b/generators/swift/sdk/versions.yml @@ -1,4 +1,14 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 0.36.1 + changelogEntry: + - summary: | + Endpoints configured with `x-fern-retries: { disabled: true }` (or `retries: { disabled: true }` in a Fern + definition) are no longer retried. The generated endpoint method passes `retriesDisabled: true` to the + HTTP client, which takes precedence over both the client-level `maxRetries` and the per-request + `RequestOptions.maxRetries`. + type: fix + createdAt: "2026-08-20" + irVersion: 66 - version: 0.36.0 changelogEntry: - summary: | diff --git a/generators/typescript/sdk/changes/3.88.1/honor-x-fern-retries-disabled.yml b/generators/typescript/sdk/changes/3.88.1/honor-x-fern-retries-disabled.yml new file mode 100644 index 000000000000..c484f7d942fa --- /dev/null +++ b/generators/typescript/sdk/changes/3.88.1/honor-x-fern-retries-disabled.yml @@ -0,0 +1,7 @@ +- summary: | + Endpoints that disable retries (`x-fern-retries: { disabled: true }` in OpenAPI, or + `retries: { disabled: true }` in a Fern definition) now always emit `maxRetries: 0`, + overriding both client-level and request-level `maxRetries`. Previously this + configuration was only honored by the Python SDK generator and was silently ignored + in TypeScript. + type: fix diff --git a/generators/typescript/sdk/client-class-generator/src/__test__/requestOptionsParameter.test.ts b/generators/typescript/sdk/client-class-generator/src/__test__/requestOptionsParameter.test.ts index 93e88516ecab..d3fe17a9af5f 100644 --- a/generators/typescript/sdk/client-class-generator/src/__test__/requestOptionsParameter.test.ts +++ b/generators/typescript/sdk/client-class-generator/src/__test__/requestOptionsParameter.test.ts @@ -96,33 +96,34 @@ describe("requestOptionsParameter", () => { }); describe("getMaxRetriesExpression", () => { - it("generates max retries with nullish coalescing", () => { - const referenceToOptions = ts.factory.createPropertyAccessExpression( - ts.factory.createThis(), - ts.factory.createIdentifier("_options") - ); + const referenceToOptions = ts.factory.createPropertyAccessExpression( + ts.factory.createThis(), + ts.factory.createIdentifier("_options") + ); - const maxRetriesReference = ({ - referenceToRequestOptions, - isNullable - }: { - referenceToRequestOptions: ts.Expression; - isNullable: boolean; - }) => { - if (isNullable) { - return ts.factory.createPropertyAccessChain( - referenceToRequestOptions, - ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), - ts.factory.createIdentifier("maxRetries") - ); - } - return ts.factory.createPropertyAccessExpression( + const maxRetriesReference = ({ + referenceToRequestOptions, + isNullable + }: { + referenceToRequestOptions: ts.Expression; + isNullable: boolean; + }) => { + if (isNullable) { + return ts.factory.createPropertyAccessChain( referenceToRequestOptions, + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), ts.factory.createIdentifier("maxRetries") ); - }; + } + return ts.factory.createPropertyAccessExpression( + referenceToRequestOptions, + ts.factory.createIdentifier("maxRetries") + ); + }; + it("generates max retries with nullish coalescing", () => { const result = getMaxRetriesExpression({ + endpoint: { retries: undefined }, maxRetriesReference, referenceToOptions }); @@ -130,6 +131,16 @@ describe("requestOptionsParameter", () => { const text = getTextOfTsNode(result); expect(text).toMatchSnapshot(); }); + + it("generates zero when the endpoint disables retries", () => { + const result = getMaxRetriesExpression({ + endpoint: { retries: { disabled: true } }, + maxRetriesReference, + referenceToOptions + }); + + expect(getTextOfTsNode(result)).toBe("0"); + }); }); describe("getAbortSignalExpression", () => { diff --git a/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedFileDownloadEndpointImplementation.ts b/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedFileDownloadEndpointImplementation.ts index df0ba9da185f..6f32695063e0 100644 --- a/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedFileDownloadEndpointImplementation.ts +++ b/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedFileDownloadEndpointImplementation.ts @@ -224,6 +224,7 @@ export class GeneratedFileDownloadEndpointImplementation implements GeneratedEnd referenceToOptions: this.generatedSdkClientClass.getReferenceToOptions() }), maxRetries: getMaxRetriesExpression({ + endpoint: this.endpoint, maxRetriesReference: this.generatedSdkClientClass.getReferenceToMaxRetries.bind( this.generatedSdkClientClass ), diff --git a/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedStreamingEndpointImplementation.ts b/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedStreamingEndpointImplementation.ts index 29ef6079931c..e5ca53e69ebe 100644 --- a/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedStreamingEndpointImplementation.ts +++ b/generators/typescript/sdk/client-class-generator/src/endpoints/GeneratedStreamingEndpointImplementation.ts @@ -302,6 +302,7 @@ export class GeneratedStreamingEndpointImplementation implements GeneratedEndpoi referenceToOptions: this.generatedSdkClientClass.getReferenceToOptions() }), maxRetries: getMaxRetriesExpression({ + endpoint: this.endpoint, maxRetriesReference: this.generatedSdkClientClass.getReferenceToMaxRetries.bind( this.generatedSdkClientClass ), diff --git a/generators/typescript/sdk/client-class-generator/src/endpoints/default/GeneratedDefaultEndpointImplementation.ts b/generators/typescript/sdk/client-class-generator/src/endpoints/default/GeneratedDefaultEndpointImplementation.ts index e6c4c7a53c0f..36b8d23a0606 100644 --- a/generators/typescript/sdk/client-class-generator/src/endpoints/default/GeneratedDefaultEndpointImplementation.ts +++ b/generators/typescript/sdk/client-class-generator/src/endpoints/default/GeneratedDefaultEndpointImplementation.ts @@ -824,6 +824,7 @@ export class GeneratedDefaultEndpointImplementation implements GeneratedEndpoint } const maxRetriesExpression = getMaxRetriesExpression({ + endpoint: this.endpoint, maxRetriesReference: this.generatedSdkClientClass.getReferenceToMaxRetries.bind( this.generatedSdkClientClass ), @@ -961,6 +962,7 @@ export class GeneratedDefaultEndpointImplementation implements GeneratedEndpoint referenceToOptions: this.generatedSdkClientClass.getReferenceToOptions() }), maxRetries: getMaxRetriesExpression({ + endpoint: this.endpoint, maxRetriesReference: this.generatedSdkClientClass.getReferenceToMaxRetries.bind( this.generatedSdkClientClass ), diff --git a/generators/typescript/sdk/client-class-generator/src/endpoints/utils/requestOptionsParameter.ts b/generators/typescript/sdk/client-class-generator/src/endpoints/utils/requestOptionsParameter.ts index 55f4f54ba355..68877d1807f1 100644 --- a/generators/typescript/sdk/client-class-generator/src/endpoints/utils/requestOptionsParameter.ts +++ b/generators/typescript/sdk/client-class-generator/src/endpoints/utils/requestOptionsParameter.ts @@ -1,3 +1,4 @@ +import { FernIr } from "@fern-fern/ir-sdk"; import { getTextOfTsNode } from "@fern-typescript/commons"; import { OptionalKind, ParameterDeclarationStructure, ts } from "ts-morph"; @@ -109,12 +110,19 @@ export const getTimeoutExpression = ({ }; export const getMaxRetriesExpression = ({ + endpoint, maxRetriesReference, referenceToOptions }: { + endpoint: Pick; maxRetriesReference: (args: { referenceToRequestOptions: ts.Expression; isNullable: boolean }) => ts.Expression; referenceToOptions: ts.Expression; }): ts.Expression => { + // Endpoints with retries explicitly disabled never retry, regardless of client- or request-level config. + if (endpoint.retries?.disabled === true) { + return ts.factory.createNumericLiteral("0"); + } + const requestOptionsMaxRetries = maxRetriesReference({ referenceToRequestOptions: ts.factory.createIdentifier(REQUEST_OPTIONS_PARAMETER_NAME), isNullable: true diff --git a/generators/typescript/sdk/versions.yml b/generators/typescript/sdk/versions.yml index b508a3488ff3..3674018d2265 100644 --- a/generators/typescript/sdk/versions.yml +++ b/generators/typescript/sdk/versions.yml @@ -1,4 +1,15 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 3.88.1 + changelogEntry: + - summary: | + Endpoints that disable retries (`x-fern-retries: { disabled: true }` in OpenAPI, or + `retries: { disabled: true }` in a Fern definition) now always emit `maxRetries: 0`, + overriding both client-level and request-level `maxRetries`. Previously this + configuration was only honored by the Python SDK generator and was silently ignored + in TypeScript. + type: fix + createdAt: "2026-08-20" + irVersion: 67 - version: 3.88.0 changelogEntry: - summary: | diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_Cursor.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_Cursor.json new file mode 100644 index 000000000000..0b03818cb153 --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_Cursor.json @@ -0,0 +1,12 @@ +{ + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "A named alias that resolves to an optional string, i.e. a pointer in Go.", + "definitions": {} +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersAliasCursorResponse.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersAliasCursorResponse.json new file mode 100644 index 000000000000..dd69c1ad625a --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersAliasCursorResponse.json @@ -0,0 +1,47 @@ +{ + "type": "object", + "properties": { + "next_cursor": { + "$ref": "#/definitions/users.Cursor" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/users.User" + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false, + "definitions": { + "users.Cursor": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "A named alias that resolves to an optional string, i.e. a pointer in Go." + }, + "users.User": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "integer" + } + }, + "required": [ + "name", + "id" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersNullableAliasCursorResponse.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersNullableAliasCursorResponse.json new file mode 100644 index 000000000000..bb235449b501 --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersNullableAliasCursorResponse.json @@ -0,0 +1,48 @@ +{ + "type": "object", + "properties": { + "next_cursor": { + "$ref": "#/definitions/users.NullableCursor" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/users.User" + } + } + }, + "required": [ + "next_cursor", + "data" + ], + "additionalProperties": false, + "definitions": { + "users.NullableCursor": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "A named alias that resolves to a nullable string, which is also a pointer in Go." + }, + "users.User": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "integer" + } + }, + "required": [ + "name", + "id" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersRequiredAliasCursorResponse.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersRequiredAliasCursorResponse.json new file mode 100644 index 000000000000..c2ac1ee4094b --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_ListUsersRequiredAliasCursorResponse.json @@ -0,0 +1,41 @@ +{ + "type": "object", + "properties": { + "next_cursor": { + "$ref": "#/definitions/users.RequiredCursor" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/users.User" + } + } + }, + "required": [ + "next_cursor", + "data" + ], + "additionalProperties": false, + "definitions": { + "users.RequiredCursor": { + "type": "string", + "description": "A named alias that resolves to a required string, i.e. a value in Go." + }, + "users.User": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "integer" + } + }, + "required": [ + "name", + "id" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_NullableCursor.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_NullableCursor.json new file mode 100644 index 000000000000..9e0c7775b179 --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_NullableCursor.json @@ -0,0 +1,12 @@ +{ + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "A named alias that resolves to a nullable string, which is also a pointer in Go.", + "definitions": {} +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_RequiredCursor.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_RequiredCursor.json new file mode 100644 index 000000000000..777ffb35098c --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/go-request-body-pagination/type_users_RequiredCursor.json @@ -0,0 +1,5 @@ +{ + "type": "string", + "description": "A named alias that resolves to a required string, i.e. a value in Go.", + "definitions": {} +} \ No newline at end of file diff --git a/packages/commons/mock-utils/src/index.ts b/packages/commons/mock-utils/src/index.ts index 66bc77e7617b..7ae95cdc8754 100644 --- a/packages/commons/mock-utils/src/index.ts +++ b/packages/commons/mock-utils/src/index.ts @@ -319,8 +319,18 @@ export class WireMock { // matchers entirely in that mode; per-endpoint header enforcement is done // explicitly in the generated wire tests (verify_auth_headers). const isEndpointSecurity = ir.auth.requirement === FernIr.AuthSchemesRequirement.EndpointSecurity; + // The OAuth token (and refresh) endpoint is called to *obtain* a token, so its + // request can never carry the Authorization header the other stubs require — + // even when the spec marks the endpoint as authed. Skip auth matchers for it. + const isOAuthTokenExchangeEndpoint = ir.auth.schemes.some( + (scheme) => + scheme.type === "oauth" && + scheme.configuration.type === "clientCredentials" && + (scheme.configuration.tokenEndpoint.endpointReference.endpointId === endpoint.id || + scheme.configuration.refreshEndpoint?.endpointReference.endpointId === endpoint.id) + ); const authHeaders: Record = {}; - if (endpoint.auth && !isEndpointSecurity) { + if (endpoint.auth && !isEndpointSecurity && !isOAuthTokenExchangeEndpoint) { // Multiple schemes can write the Authorization header (oauth, bearer, basic). // Which value the client sends when several are configured (e.g. `auth: any`) // is language-dependent (some send the OAuth Bearer token, others Basic diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca2eb4121d0b..40e91a2d1e21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -587,7 +587,7 @@ overrides: minimatch: '>=10.2.3' qs: 6.15.2 url-join: ^4.0.1 - '@fern-api/fdr-sdk': 1.2.92-64cea478ee + '@fern-api/fdr-sdk': 1.2.96-e7835c2e07 form-data: ^4.0.6 '@fern-api/ui-core-utils': 0.145.12-b50d999d1 vite: ^7.3.5 @@ -3915,8 +3915,8 @@ importers: packages/cli/api-importers/graphql: dependencies: '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../../commons/fs-utils @@ -4291,8 +4291,8 @@ importers: specifier: workspace:* version: link:../../configuration '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/ir-generator': specifier: workspace:* version: link:../../generation/ir-generator @@ -4418,8 +4418,8 @@ importers: specifier: workspace:* version: link:../yaml/docs-validator '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fern-definition-formatter': specifier: workspace:* version: link:../fern-definition/formatter @@ -4876,8 +4876,8 @@ importers: specifier: workspace:* version: link:../yaml/docs-validator '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fern-definition-schema': specifier: workspace:* version: link:../fern-definition/schema @@ -5081,8 +5081,8 @@ importers: specifier: workspace:* version: link:../../commons/core-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fern-definition-schema': specifier: workspace:* version: link:../fern-definition/schema @@ -5118,8 +5118,8 @@ importers: specifier: workspace:* version: link:../../commons/core-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -5197,8 +5197,8 @@ importers: specifier: workspace:* version: link:../../configuration '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../../commons/fs-utils @@ -5237,8 +5237,8 @@ importers: specifier: workspace:* version: link:../commons '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../../commons/fs-utils @@ -5277,8 +5277,8 @@ importers: specifier: workspace:* version: link:../commons '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../../commons/fs-utils @@ -5344,8 +5344,8 @@ importers: packages/cli/docs-markdown-utils: dependencies: '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -5435,8 +5435,8 @@ importers: specifier: workspace:* version: link:../docs-resolver '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -5550,8 +5550,8 @@ importers: specifier: workspace:* version: link:../docs-markdown-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -5653,8 +5653,8 @@ importers: specifier: workspace:* version: link:../configuration '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -6361,8 +6361,8 @@ importers: specifier: 'catalog:' version: 0.0.6-2ee1b7e28 '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../../../commons/fs-utils @@ -6586,8 +6586,8 @@ importers: specifier: workspace:* version: link:../generation/local-generation/docker-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -6832,8 +6832,8 @@ importers: specifier: workspace:* version: link:../../commons/core-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fs-utils': specifier: workspace:* version: link:../../commons/fs-utils @@ -7028,8 +7028,8 @@ importers: specifier: workspace:* version: link:../../../commons/core-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/ir-sdk': specifier: workspace:* version: link:../../../ir-sdk @@ -7113,8 +7113,8 @@ importers: specifier: workspace:* version: link:../../../commons/core-utils '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fern-definition-schema': specifier: workspace:* version: link:../../fern-definition/schema @@ -7359,8 +7359,8 @@ importers: specifier: workspace:* version: link:../../docs-resolver '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/fern-definition-schema': specifier: workspace:* version: link:../../fern-definition/schema @@ -8004,8 +8004,8 @@ importers: packages/core: dependencies: '@fern-api/fdr-sdk': - specifier: 1.2.92-64cea478ee - version: 1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3) + specifier: 1.2.96-e7835c2e07 + version: 1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3) '@fern-api/venus-api-sdk': specifier: 'catalog:' version: 5.0.0 @@ -9558,8 +9558,8 @@ packages: resolution: {integrity: sha512-3qhAAuc4ZJWLaFtyZzaYXfF9OQ5iviNrvLDXtjKScKUNS134fR3v3c3xedCidTq5KedapuBECziUaOmmd6KXVA==} engines: {node: '>=18.0.0'} - '@fern-api/fdr-sdk@1.2.92-64cea478ee': - resolution: {integrity: sha512-yCHZAbOJbl60d4o/focZbaX71Mx1zEyZvnu1U5pF8qadA91L3zAMcQ7jKEn6+wN/v5rmqCFNbBMQ3rB9LSBAFw==} + '@fern-api/fdr-sdk@1.2.96-e7835c2e07': + resolution: {integrity: sha512-KNP3ifbZzpOd2U1fkXudVz+p7+KqRzzfUtw0594M+sK/x+UdYyatFW8qQq7N2ilKFbJUZZS0Lnzq1pA36/jmRg==} '@fern-api/generator-cli@0.9.53': resolution: {integrity: sha512-737p5KbB8DZQFhU3JhW6hvI2If2O8ySDUb4bWXB1OzFT/dhAqSxhCDpAp9vrQH53ldtEEzAYqR44yAxw/0wUgA==} @@ -16523,7 +16523,7 @@ snapshots: '@fern-api/fai-sdk@0.0.6-2ee1b7e28': {} - '@fern-api/fdr-sdk@1.2.92-64cea478ee(@opentelemetry/api@1.9.1)(typescript@5.9.3)': + '@fern-api/fdr-sdk@1.2.96-e7835c2e07(@opentelemetry/api@1.9.1)(typescript@5.9.3)': dependencies: '@fern-api/ui-core-utils': 0.145.12-b50d999d1 '@orpc/client': 1.13.9(@opentelemetry/api@1.9.1) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7c1b3fd83981..20ea147802f6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -61,7 +61,7 @@ catalog: "@bufbuild/protobuf": ^2.2.5 "@bufbuild/protoplugin": 2.2.5 "@fern-api/fai-sdk": 0.0.6-2ee1b7e28 - "@fern-api/fdr-sdk": 1.2.92-64cea478ee + "@fern-api/fdr-sdk": 1.2.96-e7835c2e07 "@fern-api/generator-cli": 0.9.53 "@fern-api/ui-core-utils": 0.129.4-b6c699ad2 "@fern-api/venus-api-sdk": 5.0.0 @@ -263,7 +263,7 @@ overrides: minimatch: ">=10.2.3" qs: 6.15.2 url-join: ^4.0.1 - "@fern-api/fdr-sdk": 1.2.92-64cea478ee + "@fern-api/fdr-sdk": 1.2.96-e7835c2e07 form-data: ^4.0.6 "@fern-api/ui-core-utils": 0.145.12-b50d999d1 vite: ^7.3.5 diff --git a/seed/cli/query-parameters-openapi/github-npm/.github/workflows/release.yml b/seed/cli/query-parameters-openapi/github-npm/.github/workflows/release.yml index 9c0264b06ace..378af837090b 100644 --- a/seed/cli/query-parameters-openapi/github-npm/.github/workflows/release.yml +++ b/seed/cli/query-parameters-openapi/github-npm/.github/workflows/release.yml @@ -315,7 +315,22 @@ jobs: # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + # Re-runs and pre-created releases must not fail after a successful build. + # --draft=false publishes a release that was drafted in the UI: + # gh release view finds drafts, so without it the assets upload into + # a release nobody can see and the job still reports success. A draft + # has no tag yet, so --target decides where that tag lands; for an + # already-published release the tag exists and GitHub ignores it. + if gh release view "${{ needs.plan.outputs.tag }}" > /dev/null 2>&1; then + if [[ -n "$PRERELEASE_FLAG" ]]; then + gh release edit "${{ needs.plan.outputs.tag }}" --title "$ANNOUNCEMENT_TITLE" --target "$RELEASE_COMMIT" --notes-file "$RUNNER_TEMP/notes.txt" --draft=false --prerelease + else + gh release edit "${{ needs.plan.outputs.tag }}" --title "$ANNOUNCEMENT_TITLE" --target "$RELEASE_COMMIT" --notes-file "$RUNNER_TEMP/notes.txt" --draft=false --prerelease=false + fi + gh release upload "${{ needs.plan.outputs.tag }}" artifacts/* --clobber + else + gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + fi announce: needs: diff --git a/seed/csharp-sdk/no-retries/src/SeedNoRetries.Test/Core/RawClientTests/RetriesTests.cs b/seed/csharp-sdk/no-retries/src/SeedNoRetries.Test/Core/RawClientTests/RetriesTests.cs index bf1262d87b48..5fa01e895260 100644 --- a/seed/csharp-sdk/no-retries/src/SeedNoRetries.Test/Core/RawClientTests/RetriesTests.cs +++ b/seed/csharp-sdk/no-retries/src/SeedNoRetries.Test/Core/RawClientTests/RetriesTests.cs @@ -515,6 +515,31 @@ public async SystemTask SendRequestAsync_ShouldRetry_WhenHandlerDisposesRequestC } } + [Test] + public async SystemTask SendRequestAsync_ShouldNotRetry_WhenRetriesDisabledForEndpoint() + { + _server + .Given(WireMockRequest.Create().WithPath("/test").UsingGet()) + .RespondWith(WireMockResponse.Create().WithStatusCode(500).WithBody("Failure")); + + var request = new SeedNoRetries.Core.EmptyRequest + { + BaseUrl = _baseUrl, + Method = HttpMethod.Get, + Path = "/test", + Options = new global::SeedNoRetries.RequestOptions { MaxRetries = MaxRetries }, + RetriesDisabled = true, + }; + + var response = await _rawClient.SendRequestAsync(request); + + using (Assert.EnterMultipleScope()) + { + Assert.That(response.StatusCode, Is.EqualTo(500)); + Assert.That(_server.LogEntries, Has.Count.EqualTo(1)); + } + } + [TearDown] public void TearDown() { diff --git a/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/BaseRequest.cs b/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/BaseRequest.cs index 41d500d6a9cc..609717829037 100644 --- a/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/BaseRequest.cs +++ b/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/BaseRequest.cs @@ -24,6 +24,11 @@ internal abstract record BaseRequest internal IRequestOptions? Options { get; init; } + /// + /// Whether retries are disabled for this request, regardless of the client or request options. + /// + internal bool RetriesDisabled { get; init; } + internal abstract HttpContent? CreateContent(); protected static ( diff --git a/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/RawClient.cs b/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/RawClient.cs index f1b48f70e874..afc18dab7a8a 100644 --- a/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/RawClient.cs +++ b/seed/csharp-sdk/no-retries/src/SeedNoRetries/Core/RawClient.cs @@ -37,14 +37,20 @@ internal partial class RawClient(ClientOptions clientOptions) var httpRequest = await CreateHttpRequestAsync(request).ConfigureAwait(false); // Send the request. - return await SendWithRetriesAsync(httpRequest, request.Options, cts.Token) + return await SendWithRetriesAsync( + httpRequest, + request.Options, + cts.Token, + request.RetriesDisabled + ) .ConfigureAwait(false); } internal async global::System.Threading.Tasks.Task SendRequestAsync( HttpRequestMessage request, IRequestOptions? options, - CancellationToken cancellationToken = default + CancellationToken cancellationToken = default, + bool retriesDisabled = false ) { // Apply the request timeout. @@ -53,7 +59,8 @@ internal partial class RawClient(ClientOptions clientOptions) cts.CancelAfter(timeout); // Send the request. - return await SendWithRetriesAsync(request, options, cts.Token).ConfigureAwait(false); + return await SendWithRetriesAsync(request, options, cts.Token, retriesDisabled) + .ConfigureAwait(false); } private static async global::System.Threading.Tasks.Task CloneRequestAsync( @@ -137,11 +144,14 @@ await request private async global::System.Threading.Tasks.Task SendWithRetriesAsync( HttpRequestMessage request, IRequestOptions? options, - CancellationToken cancellationToken + CancellationToken cancellationToken, + bool retriesDisabled ) { var httpClient = options?.HttpClient ?? Options.HttpClient; - var maxRetries = Math.Max(0, options?.MaxRetries ?? Options.MaxRetries); + var maxRetries = retriesDisabled + ? 0 + : Math.Max(0, options?.MaxRetries ?? Options.MaxRetries); var isRetryableContent = IsRetryableContent(request); if (!isRetryableContent || maxRetries == 0) diff --git a/seed/csharp-sdk/no-retries/src/SeedNoRetries/Retries/RetriesClient.cs b/seed/csharp-sdk/no-retries/src/SeedNoRetries/Retries/RetriesClient.cs index 1f4e87577089..6e70f9f0b2f9 100644 --- a/seed/csharp-sdk/no-retries/src/SeedNoRetries/Retries/RetriesClient.cs +++ b/seed/csharp-sdk/no-retries/src/SeedNoRetries/Retries/RetriesClient.cs @@ -35,6 +35,7 @@ private async Task>> GetUsersAsyncCore( QueryString = _queryString, Headers = _headers, Options = options, + RetriesDisabled = true, }, cancellationToken ) diff --git a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example3/snippet.go b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example3/snippet.go index ee4e29c7f5a2..a29d34ff2afa 100644 --- a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example3/snippet.go +++ b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example3/snippet.go @@ -14,15 +14,12 @@ func do() { "https://api.fern.com", ), ) - request := &fern.ListUsersBodyOffsetPaginationRequest{ - Page: fern.Int( - 1, - ), - Limit: fern.Int( - 1, + request := &fern.ListUsersAliasBodyCursorPaginationRequest{ + Cursor: fern.String( + "cursor", ), } - client.Users.ListWithBodyOffsetPagination( + client.Users.ListWithAliasBodyCursorPagination( context.TODO(), request, ) diff --git a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example4/snippet.go b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example4/snippet.go index ea57afe246b8..f7e37280ab55 100644 --- a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example4/snippet.go +++ b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example4/snippet.go @@ -14,14 +14,10 @@ func do() { "https://api.fern.com", ), ) - request := &fern.ListUsersNestedBodyCursorPaginationRequest{ - Pagination: &fern.WithCursor{ - Cursor: fern.String( - "cursor", - ), - }, + request := &fern.ListUsersRequiredAliasBodyCursorPaginationRequest{ + Cursor: "cursor", } - client.Users.ListWithNestedBodyCursorPagination( + client.Users.ListWithRequiredAliasBodyCursorPagination( context.TODO(), request, ) diff --git a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example5/snippet.go b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example5/snippet.go new file mode 100644 index 000000000000..95455fb81d8d --- /dev/null +++ b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example5/snippet.go @@ -0,0 +1,26 @@ +package example + +import ( + context "context" + + fern "github.com/go-request-body-pagination/fern" + client "github.com/go-request-body-pagination/fern/client" + option "github.com/go-request-body-pagination/fern/option" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + ) + request := &fern.ListUsersNullableAliasBodyCursorPaginationRequest{ + Cursor: fern.String( + "cursor", + ), + } + client.Users.ListWithNullableAliasBodyCursorPagination( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example6/snippet.go b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example6/snippet.go new file mode 100644 index 000000000000..ee4e29c7f5a2 --- /dev/null +++ b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example6/snippet.go @@ -0,0 +1,29 @@ +package example + +import ( + context "context" + + fern "github.com/go-request-body-pagination/fern" + client "github.com/go-request-body-pagination/fern/client" + option "github.com/go-request-body-pagination/fern/option" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + ) + request := &fern.ListUsersBodyOffsetPaginationRequest{ + Page: fern.Int( + 1, + ), + Limit: fern.Int( + 1, + ), + } + client.Users.ListWithBodyOffsetPagination( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example7/snippet.go b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example7/snippet.go new file mode 100644 index 000000000000..ea57afe246b8 --- /dev/null +++ b/seed/go-sdk/go-request-body-pagination/dynamic-snippets/example7/snippet.go @@ -0,0 +1,28 @@ +package example + +import ( + context "context" + + fern "github.com/go-request-body-pagination/fern" + client "github.com/go-request-body-pagination/fern/client" + option "github.com/go-request-body-pagination/fern/option" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "https://api.fern.com", + ), + ) + request := &fern.ListUsersNestedBodyCursorPaginationRequest{ + Pagination: &fern.WithCursor{ + Cursor: fern.String( + "cursor", + ), + }, + } + client.Users.ListWithNestedBodyCursorPagination( + context.TODO(), + request, + ) +} diff --git a/seed/go-sdk/go-request-body-pagination/reference.md b/seed/go-sdk/go-request-body-pagination/reference.md index f20312de7c5d..8b2bb2c99a4b 100644 --- a/seed/go-sdk/go-request-body-pagination/reference.md +++ b/seed/go-sdk/go-request-body-pagination/reference.md @@ -195,6 +195,193 @@ client.Users.ListWithUUIDBodyCursorPagination( + + + + +
client.Users.ListWithAliasBodyCursorPagination(request) -> *fern.ListUsersAliasCursorResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Pagination endpoint whose cursor is a named alias resolving to an optional string. The alias +generates as a pointer in Go, so an empty string cursor terminates the pager here too. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.ListUsersAliasBodyCursorPaginationRequest{ + Cursor: fern.String( + "cursor", + ), +} +client.Users.ListWithAliasBodyCursorPagination( + context.TODO(), + request, +) +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**cursor:** `fern.Cursor` + +
+
+
+
+ + +
+
+
+ +
client.Users.ListWithRequiredAliasBodyCursorPagination(request) -> *fern.ListUsersRequiredAliasCursorResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Pagination endpoint whose cursor is a named alias resolving to a required string. The alias +generates as a value in Go, where the zero value is already the empty string. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.ListUsersRequiredAliasBodyCursorPaginationRequest{ + Cursor: "cursor", +} +client.Users.ListWithRequiredAliasBodyCursorPagination( + context.TODO(), + request, +) +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**cursor:** `fern.RequiredCursor` + +
+
+
+
+ + +
+
+
+ +
client.Users.ListWithNullableAliasBodyCursorPagination(request) -> *fern.ListUsersNullableAliasCursorResponse +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Pagination endpoint whose cursor is a named alias resolving to a nullable string. Nullable +generates as a pointer in Go, so an empty string cursor terminates the pager here too. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```go +request := &fern.ListUsersNullableAliasBodyCursorPaginationRequest{ + Cursor: fern.String( + "cursor", + ), +} +client.Users.ListWithNullableAliasBodyCursorPagination( + context.TODO(), + request, +) +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**cursor:** `fern.NullableCursor` + +
+
+
+
+ +
diff --git a/seed/go-sdk/go-request-body-pagination/snippet.json b/seed/go-sdk/go-request-body-pagination/snippet.json index de59f169df94..2edca8b6d7a6 100644 --- a/seed/go-sdk/go-request-body-pagination/snippet.json +++ b/seed/go-sdk/go-request-body-pagination/snippet.json @@ -11,6 +11,17 @@ "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/go-request-body-pagination/fern\"\n\tfernclient \"github.com/go-request-body-pagination/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Users.ListWithBodyCursorPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersBodyCursorPaginationRequest{\n\t\tCursor: fern.String(\n\t\t\t\"cursor\",\n\t\t),\n\t\tFilter: fern.String(\n\t\t\t\"filter\",\n\t\t),\n\t},\n)\n" } }, + { + "id": { + "path": "/users/alias-cursor", + "method": "POST", + "identifier_override": "endpoint_users.listWithAliasBodyCursorPagination" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/go-request-body-pagination/fern\"\n\tfernclient \"github.com/go-request-body-pagination/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Users.ListWithAliasBodyCursorPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersAliasBodyCursorPaginationRequest{\n\t\tCursor: fern.String(\n\t\t\t\"cursor\",\n\t\t),\n\t},\n)\n" + } + }, { "id": { "path": "/users/nested-cursor", @@ -22,6 +33,17 @@ "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/go-request-body-pagination/fern\"\n\tfernclient \"github.com/go-request-body-pagination/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Users.ListWithNestedBodyCursorPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersNestedBodyCursorPaginationRequest{\n\t\tPagination: \u0026fern.WithCursor{\n\t\t\tCursor: fern.String(\n\t\t\t\t\"cursor\",\n\t\t\t),\n\t\t},\n\t},\n)\n" } }, + { + "id": { + "path": "/users/nullable-alias-cursor", + "method": "POST", + "identifier_override": "endpoint_users.listWithNullableAliasBodyCursorPagination" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/go-request-body-pagination/fern\"\n\tfernclient \"github.com/go-request-body-pagination/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Users.ListWithNullableAliasBodyCursorPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersNullableAliasBodyCursorPaginationRequest{\n\t\tCursor: fern.String(\n\t\t\t\"cursor\",\n\t\t),\n\t},\n)\n" + } + }, { "id": { "path": "/users/offset", @@ -33,6 +55,17 @@ "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/go-request-body-pagination/fern\"\n\tfernclient \"github.com/go-request-body-pagination/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Users.ListWithBodyOffsetPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersBodyOffsetPaginationRequest{\n\t\tPage: fern.Int(\n\t\t\t1,\n\t\t),\n\t\tLimit: fern.Int(\n\t\t\t1,\n\t\t),\n\t},\n)\n" } }, + { + "id": { + "path": "/users/required-alias-cursor", + "method": "POST", + "identifier_override": "endpoint_users.listWithRequiredAliasBodyCursorPagination" + }, + "snippet": { + "type": "go", + "client": "import (\n\tcontext \"context\"\n\tfern \"github.com/go-request-body-pagination/fern\"\n\tfernclient \"github.com/go-request-body-pagination/fern/client\"\n)\n\nclient := fernclient.NewClient()\nresponse, err := client.Users.ListWithRequiredAliasBodyCursorPagination(\n\tcontext.TODO(),\n\t\u0026fern.ListUsersRequiredAliasBodyCursorPaginationRequest{\n\t\tCursor: \"cursor\",\n\t},\n)\n" + } + }, { "id": { "path": "/users/required-cursor", diff --git a/seed/go-sdk/go-request-body-pagination/users.go b/seed/go-sdk/go-request-body-pagination/users.go index 47092bfdfd59..64ff62c816a2 100644 --- a/seed/go-sdk/go-request-body-pagination/users.go +++ b/seed/go-sdk/go-request-body-pagination/users.go @@ -10,6 +10,52 @@ import ( big "math/big" ) +var ( + listUsersAliasBodyCursorPaginationRequestFieldCursor = big.NewInt(1 << 0) +) + +type ListUsersAliasBodyCursorPaginationRequest struct { + Cursor Cursor `json:"cursor,omitempty" url:"-"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +func (l *ListUsersAliasBodyCursorPaginationRequest) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetCursor sets the Cursor field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersAliasBodyCursorPaginationRequest) SetCursor(cursor Cursor) { + l.Cursor = cursor + l.require(listUsersAliasBodyCursorPaginationRequestFieldCursor) +} + +func (l *ListUsersAliasBodyCursorPaginationRequest) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersAliasBodyCursorPaginationRequest + var body unmarshaler + if err := json.Unmarshal(data, &body); err != nil { + return err + } + *l = ListUsersAliasBodyCursorPaginationRequest(body) + return nil +} + +func (l *ListUsersAliasBodyCursorPaginationRequest) MarshalJSON() ([]byte, error) { + type embed ListUsersAliasBodyCursorPaginationRequest + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + var ( listUsersBodyCursorPaginationRequestFieldCursor = big.NewInt(1 << 0) listUsersBodyCursorPaginationRequestFieldFilter = big.NewInt(1 << 1) @@ -166,6 +212,98 @@ func (l *ListUsersNestedBodyCursorPaginationRequest) MarshalJSON() ([]byte, erro return json.Marshal(explicitMarshaler) } +var ( + listUsersNullableAliasBodyCursorPaginationRequestFieldCursor = big.NewInt(1 << 0) +) + +type ListUsersNullableAliasBodyCursorPaginationRequest struct { + Cursor NullableCursor `json:"cursor,omitempty" url:"-"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +func (l *ListUsersNullableAliasBodyCursorPaginationRequest) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetCursor sets the Cursor field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersNullableAliasBodyCursorPaginationRequest) SetCursor(cursor NullableCursor) { + l.Cursor = cursor + l.require(listUsersNullableAliasBodyCursorPaginationRequestFieldCursor) +} + +func (l *ListUsersNullableAliasBodyCursorPaginationRequest) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersNullableAliasBodyCursorPaginationRequest + var body unmarshaler + if err := json.Unmarshal(data, &body); err != nil { + return err + } + *l = ListUsersNullableAliasBodyCursorPaginationRequest(body) + return nil +} + +func (l *ListUsersNullableAliasBodyCursorPaginationRequest) MarshalJSON() ([]byte, error) { + type embed ListUsersNullableAliasBodyCursorPaginationRequest + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + +var ( + listUsersRequiredAliasBodyCursorPaginationRequestFieldCursor = big.NewInt(1 << 0) +) + +type ListUsersRequiredAliasBodyCursorPaginationRequest struct { + Cursor RequiredCursor `json:"cursor" url:"-"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` +} + +func (l *ListUsersRequiredAliasBodyCursorPaginationRequest) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetCursor sets the Cursor field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersRequiredAliasBodyCursorPaginationRequest) SetCursor(cursor RequiredCursor) { + l.Cursor = cursor + l.require(listUsersRequiredAliasBodyCursorPaginationRequestFieldCursor) +} + +func (l *ListUsersRequiredAliasBodyCursorPaginationRequest) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersRequiredAliasBodyCursorPaginationRequest + var body unmarshaler + if err := json.Unmarshal(data, &body); err != nil { + return err + } + *l = ListUsersRequiredAliasBodyCursorPaginationRequest(body) + return nil +} + +func (l *ListUsersRequiredAliasBodyCursorPaginationRequest) MarshalJSON() ([]byte, error) { + type embed ListUsersRequiredAliasBodyCursorPaginationRequest + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + var ( listUsersRequiredBodyCursorPaginationRequestFieldCursor = big.NewInt(1 << 0) ) @@ -258,6 +396,309 @@ func (l *ListUsersUUIDBodyCursorPaginationRequest) MarshalJSON() ([]byte, error) return json.Marshal(explicitMarshaler) } +// A named alias that resolves to an optional string, i.e. a pointer in Go. +type Cursor = *string + +var ( + listUsersAliasCursorResponseFieldNextCursor = big.NewInt(1 << 0) + listUsersAliasCursorResponseFieldData = big.NewInt(1 << 1) +) + +type ListUsersAliasCursorResponse struct { + NextCursor Cursor `json:"next_cursor,omitempty" url:"next_cursor,omitempty"` + Data []*User `json:"data" url:"data"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (l *ListUsersAliasCursorResponse) GetNextCursor() Cursor { + if l == nil { + return nil + } + return l.NextCursor +} + +func (l *ListUsersAliasCursorResponse) GetData() []*User { + if l == nil { + return nil + } + return l.Data +} + +func (l *ListUsersAliasCursorResponse) GetExtraProperties() map[string]interface{} { + if l == nil { + return nil + } + return l.extraProperties +} + +func (l *ListUsersAliasCursorResponse) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetNextCursor sets the NextCursor field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersAliasCursorResponse) SetNextCursor(nextCursor Cursor) { + l.NextCursor = nextCursor + l.require(listUsersAliasCursorResponseFieldNextCursor) +} + +// SetData sets the Data field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersAliasCursorResponse) SetData(data []*User) { + l.Data = data + l.require(listUsersAliasCursorResponseFieldData) +} + +func (l *ListUsersAliasCursorResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersAliasCursorResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListUsersAliasCursorResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) + if err != nil { + return err + } + l.extraProperties = extraProperties + l.rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListUsersAliasCursorResponse) MarshalJSON() ([]byte, error) { + type embed ListUsersAliasCursorResponse + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (l *ListUsersAliasCursorResponse) String() string { + if l == nil { + return "" + } + if len(l.rawJSON) > 0 { + if value, err := internal.StringifyJSON(l.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} + +var ( + listUsersNullableAliasCursorResponseFieldNextCursor = big.NewInt(1 << 0) + listUsersNullableAliasCursorResponseFieldData = big.NewInt(1 << 1) +) + +type ListUsersNullableAliasCursorResponse struct { + NextCursor NullableCursor `json:"next_cursor,omitempty" url:"next_cursor,omitempty"` + Data []*User `json:"data" url:"data"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (l *ListUsersNullableAliasCursorResponse) GetNextCursor() NullableCursor { + if l == nil { + return nil + } + return l.NextCursor +} + +func (l *ListUsersNullableAliasCursorResponse) GetData() []*User { + if l == nil { + return nil + } + return l.Data +} + +func (l *ListUsersNullableAliasCursorResponse) GetExtraProperties() map[string]interface{} { + if l == nil { + return nil + } + return l.extraProperties +} + +func (l *ListUsersNullableAliasCursorResponse) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetNextCursor sets the NextCursor field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersNullableAliasCursorResponse) SetNextCursor(nextCursor NullableCursor) { + l.NextCursor = nextCursor + l.require(listUsersNullableAliasCursorResponseFieldNextCursor) +} + +// SetData sets the Data field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersNullableAliasCursorResponse) SetData(data []*User) { + l.Data = data + l.require(listUsersNullableAliasCursorResponseFieldData) +} + +func (l *ListUsersNullableAliasCursorResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersNullableAliasCursorResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListUsersNullableAliasCursorResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) + if err != nil { + return err + } + l.extraProperties = extraProperties + l.rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListUsersNullableAliasCursorResponse) MarshalJSON() ([]byte, error) { + type embed ListUsersNullableAliasCursorResponse + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (l *ListUsersNullableAliasCursorResponse) String() string { + if l == nil { + return "" + } + if len(l.rawJSON) > 0 { + if value, err := internal.StringifyJSON(l.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} + +var ( + listUsersRequiredAliasCursorResponseFieldNextCursor = big.NewInt(1 << 0) + listUsersRequiredAliasCursorResponseFieldData = big.NewInt(1 << 1) +) + +type ListUsersRequiredAliasCursorResponse struct { + NextCursor RequiredCursor `json:"next_cursor" url:"next_cursor"` + Data []*User `json:"data" url:"data"` + + // Private bitmask of fields set to an explicit value and therefore not to be omitted + explicitFields *big.Int `json:"-" url:"-"` + + extraProperties map[string]interface{} + rawJSON json.RawMessage +} + +func (l *ListUsersRequiredAliasCursorResponse) GetNextCursor() RequiredCursor { + if l == nil { + return "" + } + return l.NextCursor +} + +func (l *ListUsersRequiredAliasCursorResponse) GetData() []*User { + if l == nil { + return nil + } + return l.Data +} + +func (l *ListUsersRequiredAliasCursorResponse) GetExtraProperties() map[string]interface{} { + if l == nil { + return nil + } + return l.extraProperties +} + +func (l *ListUsersRequiredAliasCursorResponse) require(field *big.Int) { + if l.explicitFields == nil { + l.explicitFields = big.NewInt(0) + } + l.explicitFields.Or(l.explicitFields, field) +} + +// SetNextCursor sets the NextCursor field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersRequiredAliasCursorResponse) SetNextCursor(nextCursor RequiredCursor) { + l.NextCursor = nextCursor + l.require(listUsersRequiredAliasCursorResponseFieldNextCursor) +} + +// SetData sets the Data field and marks it as non-optional; +// this prevents an empty or null value for this field from being omitted during serialization. +func (l *ListUsersRequiredAliasCursorResponse) SetData(data []*User) { + l.Data = data + l.require(listUsersRequiredAliasCursorResponseFieldData) +} + +func (l *ListUsersRequiredAliasCursorResponse) UnmarshalJSON(data []byte) error { + type unmarshaler ListUsersRequiredAliasCursorResponse + var value unmarshaler + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *l = ListUsersRequiredAliasCursorResponse(value) + extraProperties, err := internal.ExtractExtraProperties(data, *l) + if err != nil { + return err + } + l.extraProperties = extraProperties + l.rawJSON = json.RawMessage(data) + return nil +} + +func (l *ListUsersRequiredAliasCursorResponse) MarshalJSON() ([]byte, error) { + type embed ListUsersRequiredAliasCursorResponse + var marshaler = struct { + embed + }{ + embed: embed(*l), + } + explicitMarshaler := internal.HandleExplicitFields(marshaler, l.explicitFields) + return json.Marshal(explicitMarshaler) +} + +func (l *ListUsersRequiredAliasCursorResponse) String() string { + if l == nil { + return "" + } + if len(l.rawJSON) > 0 { + if value, err := internal.StringifyJSON(l.rawJSON); err == nil { + return value + } + } + if value, err := internal.StringifyJSON(l); err == nil { + return value + } + return fmt.Sprintf("%#v", l) +} + var ( listUsersRequiredCursorResponseFieldNextCursor = big.NewInt(1 << 0) listUsersRequiredCursorResponseFieldData = big.NewInt(1 << 1) @@ -558,6 +999,12 @@ func (l *ListUsersUUIDCursorResponse) String() string { return fmt.Sprintf("%#v", l) } +// A named alias that resolves to a nullable string, which is also a pointer in Go. +type NullableCursor = *string + +// A named alias that resolves to a required string, i.e. a value in Go. +type RequiredCursor = string + var ( userFieldName = big.NewInt(1 << 0) userFieldID = big.NewInt(1 << 1) diff --git a/seed/go-sdk/go-request-body-pagination/users/client.go b/seed/go-sdk/go-request-body-pagination/users/client.go index cde53be0cb9c..9384a1edb5e2 100644 --- a/seed/go-sdk/go-request-body-pagination/users/client.go +++ b/seed/go-sdk/go-request-body-pagination/users/client.go @@ -236,6 +236,202 @@ func (c *Client) ListWithUUIDBodyCursorPagination( return pager.GetPage(ctx, request.Cursor) } +// Pagination endpoint whose cursor is a named alias resolving to an optional string. The alias +// generates as a pointer in Go, so an empty string cursor terminates the pager here too. +// +// Example: +// +// request := &fern.ListUsersAliasBodyCursorPaginationRequest{ +// Cursor: fern.String( +// "cursor", +// ), +// } +// client.Users.ListWithAliasBodyCursorPagination( +// context.TODO(), +// request, +// ) +func (c *Client) ListWithAliasBodyCursorPagination( + ctx context.Context, + request *fern.ListUsersAliasBodyCursorPaginationRequest, + opts ...option.RequestOption, +) (*core.Page[fern.Cursor, *fern.User, *fern.ListUsersAliasCursorResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + c.baseURL, + "", + ) + endpointURL := baseURL + "/users/alias-cursor" + headers := internal.MergeHeaders( + c.options.ToHeader(), + options.ToHeader(), + ) + prepareCall := func(pageRequest *core.PageRequest[fern.Cursor]) *internal.CallParams { + nextRequest := *request + nextRequest.Cursor = pageRequest.Cursor + nextURL := endpointURL + return &internal.CallParams{ + URL: nextURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + DisableRetries: options.DisableRetries, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: &nextRequest, + Response: pageRequest.Response, + } + } + readPageResponse := func(response *fern.ListUsersAliasCursorResponse) *core.PageResponse[fern.Cursor, *fern.User, *fern.ListUsersAliasCursorResponse] { + var zeroValue fern.Cursor + next := response.GetNextCursor() + results := response.GetData() + return &core.PageResponse[fern.Cursor, *fern.User, *fern.ListUsersAliasCursorResponse]{ + Results: results, + Response: response, + Next: next, + Done: next == zeroValue || *next == "", + } + } + pager := internal.NewCursorPager( + c.caller, + prepareCall, + readPageResponse, + ) + return pager.GetPage(ctx, request.Cursor) +} + +// Pagination endpoint whose cursor is a named alias resolving to a required string. The alias +// generates as a value in Go, where the zero value is already the empty string. +// +// Example: +// +// request := &fern.ListUsersRequiredAliasBodyCursorPaginationRequest{ +// Cursor: "cursor", +// } +// client.Users.ListWithRequiredAliasBodyCursorPagination( +// context.TODO(), +// request, +// ) +func (c *Client) ListWithRequiredAliasBodyCursorPagination( + ctx context.Context, + request *fern.ListUsersRequiredAliasBodyCursorPaginationRequest, + opts ...option.RequestOption, +) (*core.Page[fern.RequiredCursor, *fern.User, *fern.ListUsersRequiredAliasCursorResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + c.baseURL, + "", + ) + endpointURL := baseURL + "/users/required-alias-cursor" + headers := internal.MergeHeaders( + c.options.ToHeader(), + options.ToHeader(), + ) + prepareCall := func(pageRequest *core.PageRequest[fern.RequiredCursor]) *internal.CallParams { + nextRequest := *request + nextRequest.Cursor = pageRequest.Cursor + nextURL := endpointURL + return &internal.CallParams{ + URL: nextURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + DisableRetries: options.DisableRetries, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: &nextRequest, + Response: pageRequest.Response, + } + } + readPageResponse := func(response *fern.ListUsersRequiredAliasCursorResponse) *core.PageResponse[fern.RequiredCursor, *fern.User, *fern.ListUsersRequiredAliasCursorResponse] { + var zeroValue fern.RequiredCursor + next := response.GetNextCursor() + results := response.GetData() + return &core.PageResponse[fern.RequiredCursor, *fern.User, *fern.ListUsersRequiredAliasCursorResponse]{ + Results: results, + Response: response, + Next: next, + Done: next == zeroValue, + } + } + pager := internal.NewCursorPager( + c.caller, + prepareCall, + readPageResponse, + ) + return pager.GetPage(ctx, request.Cursor) +} + +// Pagination endpoint whose cursor is a named alias resolving to a nullable string. Nullable +// generates as a pointer in Go, so an empty string cursor terminates the pager here too. +// +// Example: +// +// request := &fern.ListUsersNullableAliasBodyCursorPaginationRequest{ +// Cursor: fern.String( +// "cursor", +// ), +// } +// client.Users.ListWithNullableAliasBodyCursorPagination( +// context.TODO(), +// request, +// ) +func (c *Client) ListWithNullableAliasBodyCursorPagination( + ctx context.Context, + request *fern.ListUsersNullableAliasBodyCursorPaginationRequest, + opts ...option.RequestOption, +) (*core.Page[fern.NullableCursor, *fern.User, *fern.ListUsersNullableAliasCursorResponse], error) { + options := core.NewRequestOptions(opts...) + baseURL := internal.ResolveBaseURL( + options.BaseURL, + c.baseURL, + "", + ) + endpointURL := baseURL + "/users/nullable-alias-cursor" + headers := internal.MergeHeaders( + c.options.ToHeader(), + options.ToHeader(), + ) + prepareCall := func(pageRequest *core.PageRequest[fern.NullableCursor]) *internal.CallParams { + nextRequest := *request + nextRequest.Cursor = pageRequest.Cursor + nextURL := endpointURL + return &internal.CallParams{ + URL: nextURL, + Method: http.MethodPost, + Headers: headers, + MaxAttempts: options.MaxAttempts, + DisableRetries: options.DisableRetries, + BodyProperties: options.BodyProperties, + QueryParameters: options.QueryParameters, + Client: options.HTTPClient, + Request: &nextRequest, + Response: pageRequest.Response, + } + } + readPageResponse := func(response *fern.ListUsersNullableAliasCursorResponse) *core.PageResponse[fern.NullableCursor, *fern.User, *fern.ListUsersNullableAliasCursorResponse] { + var zeroValue fern.NullableCursor + next := response.GetNextCursor() + results := response.GetData() + return &core.PageResponse[fern.NullableCursor, *fern.User, *fern.ListUsersNullableAliasCursorResponse]{ + Results: results, + Response: response, + Next: next, + Done: next == zeroValue || *next == "", + } + } + pager := internal.NewCursorPager( + c.caller, + prepareCall, + readPageResponse, + ) + return pager.GetPage(ctx, request.Cursor) +} + // Pagination endpoint with a top-level page field in the request body. // // Example: diff --git a/seed/go-sdk/go-request-body-pagination/users_test.go b/seed/go-sdk/go-request-body-pagination/users_test.go index 2dade922e590..e2817a0bce37 100644 --- a/seed/go-sdk/go-request-body-pagination/users_test.go +++ b/seed/go-sdk/go-request-body-pagination/users_test.go @@ -10,6 +10,51 @@ import ( testing "testing" ) +func TestSettersListUsersAliasBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor", func(t *testing.T) { + obj := &ListUsersAliasBodyCursorPaginationRequest{} + var fernTestValueCursor Cursor + obj.SetCursor(fernTestValueCursor) + assert.Equal(t, fernTestValueCursor, obj.Cursor) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestSettersMarkExplicitListUsersAliasBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasBodyCursorPaginationRequest{} + var fernTestValueCursor Cursor + + // Act + obj.SetCursor(fernTestValueCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + func TestSettersListUsersBodyCursorPaginationRequest(t *testing.T) { t.Run("SetCursor", func(t *testing.T) { obj := &ListUsersBodyCursorPaginationRequest{} @@ -199,50 +244,583 @@ func TestSettersMarkExplicitListUsersNestedBodyCursorPaginationRequest(t *testin // Act obj.SetPagination(fernTestValuePagination) - // Assert - object with explicitly set field can be marshaled/unmarshaled - bytes, err := json.Marshal(obj) - require.NoError(t, err, "marshaling should succeed for test setup") + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersNullableAliasBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor", func(t *testing.T) { + obj := &ListUsersNullableAliasBodyCursorPaginationRequest{} + var fernTestValueCursor NullableCursor + obj.SetCursor(fernTestValueCursor) + assert.Equal(t, fernTestValueCursor, obj.Cursor) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestSettersMarkExplicitListUsersNullableAliasBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasBodyCursorPaginationRequest{} + var fernTestValueCursor NullableCursor + + // Act + obj.SetCursor(fernTestValueCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersRequiredAliasBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor", func(t *testing.T) { + obj := &ListUsersRequiredAliasBodyCursorPaginationRequest{} + var fernTestValueCursor RequiredCursor + obj.SetCursor(fernTestValueCursor) + assert.Equal(t, fernTestValueCursor, obj.Cursor) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestSettersMarkExplicitListUsersRequiredAliasBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersRequiredAliasBodyCursorPaginationRequest{} + var fernTestValueCursor RequiredCursor + + // Act + obj.SetCursor(fernTestValueCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersRequiredBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor", func(t *testing.T) { + obj := &ListUsersRequiredBodyCursorPaginationRequest{} + var fernTestValueCursor string + obj.SetCursor(fernTestValueCursor) + assert.Equal(t, fernTestValueCursor, obj.Cursor) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestSettersMarkExplicitListUsersRequiredBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersRequiredBodyCursorPaginationRequest{} + var fernTestValueCursor string + + // Act + obj.SetCursor(fernTestValueCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersUUIDBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor", func(t *testing.T) { + obj := &ListUsersUUIDBodyCursorPaginationRequest{} + var fernTestValueCursor *uuid.UUID + obj.SetCursor(fernTestValueCursor) + assert.Equal(t, fernTestValueCursor, obj.Cursor) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestSettersMarkExplicitListUsersUUIDBodyCursorPaginationRequest(t *testing.T) { + t.Run("SetCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersUUIDBodyCursorPaginationRequest{} + var fernTestValueCursor *uuid.UUID + + // Act + obj.SetCursor(fernTestValueCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersAliasCursorResponse(t *testing.T) { + t.Run("SetNextCursor", func(t *testing.T) { + obj := &ListUsersAliasCursorResponse{} + var fernTestValueNextCursor Cursor + obj.SetNextCursor(fernTestValueNextCursor) + assert.Equal(t, fernTestValueNextCursor, obj.NextCursor) + assert.NotNil(t, obj.explicitFields) + }) + + t.Run("SetData", func(t *testing.T) { + obj := &ListUsersAliasCursorResponse{} + var fernTestValueData []*User + obj.SetData(fernTestValueData) + assert.Equal(t, fernTestValueData, obj.Data) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestGettersListUsersAliasCursorResponse(t *testing.T) { + t.Run("GetNextCursor", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasCursorResponse{} + var expected Cursor + obj.NextCursor = expected + + // Act & Assert + assert.Equal(t, expected, obj.GetNextCursor(), "getter should return the property value") + }) + + t.Run("GetNextCursor_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersAliasCursorResponse + // Should not panic - getters should handle nil receiver gracefully + defer func() { + if r := recover(); r != nil { + t.Errorf("Getter panicked on nil receiver: %v", r) + } + }() + _ = obj.GetNextCursor() // Should return zero value + }) + + t.Run("GetData", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasCursorResponse{} + var expected []*User + obj.Data = expected + + // Act & Assert + assert.Equal(t, expected, obj.GetData(), "getter should return the property value") + }) + + t.Run("GetData_NilValue", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasCursorResponse{} + obj.Data = nil + + // Act & Assert + assert.Nil(t, obj.GetData(), "getter should return nil when property is nil") + }) + + t.Run("GetData_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersAliasCursorResponse + // Should not panic - getters should handle nil receiver gracefully + defer func() { + if r := recover(); r != nil { + t.Errorf("Getter panicked on nil receiver: %v", r) + } + }() + _ = obj.GetData() // Should return zero value + }) + +} + +func TestSettersMarkExplicitListUsersAliasCursorResponse(t *testing.T) { + t.Run("SetNextCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasCursorResponse{} + var fernTestValueNextCursor Cursor + + // Act + obj.SetNextCursor(fernTestValueNextCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + + t.Run("SetData_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasCursorResponse{} + var fernTestValueData []*User + + // Act + obj.SetData(fernTestValueData) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersNullableAliasCursorResponse(t *testing.T) { + t.Run("SetNextCursor", func(t *testing.T) { + obj := &ListUsersNullableAliasCursorResponse{} + var fernTestValueNextCursor NullableCursor + obj.SetNextCursor(fernTestValueNextCursor) + assert.Equal(t, fernTestValueNextCursor, obj.NextCursor) + assert.NotNil(t, obj.explicitFields) + }) + + t.Run("SetData", func(t *testing.T) { + obj := &ListUsersNullableAliasCursorResponse{} + var fernTestValueData []*User + obj.SetData(fernTestValueData) + assert.Equal(t, fernTestValueData, obj.Data) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestGettersListUsersNullableAliasCursorResponse(t *testing.T) { + t.Run("GetNextCursor", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasCursorResponse{} + var expected NullableCursor + obj.NextCursor = expected + + // Act & Assert + assert.Equal(t, expected, obj.GetNextCursor(), "getter should return the property value") + }) + + t.Run("GetNextCursor_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersNullableAliasCursorResponse + // Should not panic - getters should handle nil receiver gracefully + defer func() { + if r := recover(); r != nil { + t.Errorf("Getter panicked on nil receiver: %v", r) + } + }() + _ = obj.GetNextCursor() // Should return zero value + }) + + t.Run("GetData", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasCursorResponse{} + var expected []*User + obj.Data = expected + + // Act & Assert + assert.Equal(t, expected, obj.GetData(), "getter should return the property value") + }) + + t.Run("GetData_NilValue", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasCursorResponse{} + obj.Data = nil + + // Act & Assert + assert.Nil(t, obj.GetData(), "getter should return nil when property is nil") + }) + + t.Run("GetData_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersNullableAliasCursorResponse + // Should not panic - getters should handle nil receiver gracefully + defer func() { + if r := recover(); r != nil { + t.Errorf("Getter panicked on nil receiver: %v", r) + } + }() + _ = obj.GetData() // Should return zero value + }) + +} + +func TestSettersMarkExplicitListUsersNullableAliasCursorResponse(t *testing.T) { + t.Run("SetNextCursor_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasCursorResponse{} + var fernTestValueNextCursor NullableCursor + + // Act + obj.SetNextCursor(fernTestValueNextCursor) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + + t.Run("SetData_MarksExplicit", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasCursorResponse{} + var fernTestValueData []*User + + // Act + obj.SetData(fernTestValueData) + + // Assert - object with explicitly set field can be marshaled/unmarshaled + bytes, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed for test setup") + + // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value + // Detect if marshaled JSON is an object or primitive to use correct unmarshal target + if len(bytes) > 0 && bytes[0] == '{' { + // JSON object - unmarshal into map + var unmarshaled map[string]interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } else { + // JSON primitive (string, number, boolean, null) - unmarshal into interface{} + var unmarshaled interface{} + err = json.Unmarshal(bytes, &unmarshaled) + require.NoError(t, err, "unmarshaling should succeed for test verification") + } + + // Note: This does not explicitly assert the presence of a specific JSON field + // It verifies that setting a field via setter allows successful JSON round-trip + }) + +} + +func TestSettersListUsersRequiredAliasCursorResponse(t *testing.T) { + t.Run("SetNextCursor", func(t *testing.T) { + obj := &ListUsersRequiredAliasCursorResponse{} + var fernTestValueNextCursor RequiredCursor + obj.SetNextCursor(fernTestValueNextCursor) + assert.Equal(t, fernTestValueNextCursor, obj.NextCursor) + assert.NotNil(t, obj.explicitFields) + }) + + t.Run("SetData", func(t *testing.T) { + obj := &ListUsersRequiredAliasCursorResponse{} + var fernTestValueData []*User + obj.SetData(fernTestValueData) + assert.Equal(t, fernTestValueData, obj.Data) + assert.NotNil(t, obj.explicitFields) + }) + +} + +func TestGettersListUsersRequiredAliasCursorResponse(t *testing.T) { + t.Run("GetNextCursor", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersRequiredAliasCursorResponse{} + var expected RequiredCursor + obj.NextCursor = expected + + // Act & Assert + assert.Equal(t, expected, obj.GetNextCursor(), "getter should return the property value") + }) + + t.Run("GetNextCursor_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersRequiredAliasCursorResponse + // Should not panic - getters should handle nil receiver gracefully + defer func() { + if r := recover(); r != nil { + t.Errorf("Getter panicked on nil receiver: %v", r) + } + }() + _ = obj.GetNextCursor() // Should return zero value + }) + + t.Run("GetData", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersRequiredAliasCursorResponse{} + var expected []*User + obj.Data = expected + + // Act & Assert + assert.Equal(t, expected, obj.GetData(), "getter should return the property value") + }) - // This test ensures JSON marshaling and unmarshaling succeed when the field has a zero/nil value - // Detect if marshaled JSON is an object or primitive to use correct unmarshal target - if len(bytes) > 0 && bytes[0] == '{' { - // JSON object - unmarshal into map - var unmarshaled map[string]interface{} - err = json.Unmarshal(bytes, &unmarshaled) - require.NoError(t, err, "unmarshaling should succeed for test verification") - } else { - // JSON primitive (string, number, boolean, null) - unmarshal into interface{} - var unmarshaled interface{} - err = json.Unmarshal(bytes, &unmarshaled) - require.NoError(t, err, "unmarshaling should succeed for test verification") - } + t.Run("GetData_NilValue", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersRequiredAliasCursorResponse{} + obj.Data = nil - // Note: This does not explicitly assert the presence of a specific JSON field - // It verifies that setting a field via setter allows successful JSON round-trip + // Act & Assert + assert.Nil(t, obj.GetData(), "getter should return nil when property is nil") }) -} - -func TestSettersListUsersRequiredBodyCursorPaginationRequest(t *testing.T) { - t.Run("SetCursor", func(t *testing.T) { - obj := &ListUsersRequiredBodyCursorPaginationRequest{} - var fernTestValueCursor string - obj.SetCursor(fernTestValueCursor) - assert.Equal(t, fernTestValueCursor, obj.Cursor) - assert.NotNil(t, obj.explicitFields) + t.Run("GetData_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersRequiredAliasCursorResponse + // Should not panic - getters should handle nil receiver gracefully + defer func() { + if r := recover(); r != nil { + t.Errorf("Getter panicked on nil receiver: %v", r) + } + }() + _ = obj.GetData() // Should return zero value }) } -func TestSettersMarkExplicitListUsersRequiredBodyCursorPaginationRequest(t *testing.T) { - t.Run("SetCursor_MarksExplicit", func(t *testing.T) { +func TestSettersMarkExplicitListUsersRequiredAliasCursorResponse(t *testing.T) { + t.Run("SetNextCursor_MarksExplicit", func(t *testing.T) { t.Parallel() // Arrange - obj := &ListUsersRequiredBodyCursorPaginationRequest{} - var fernTestValueCursor string + obj := &ListUsersRequiredAliasCursorResponse{} + var fernTestValueNextCursor RequiredCursor // Act - obj.SetCursor(fernTestValueCursor) + obj.SetNextCursor(fernTestValueNextCursor) // Assert - object with explicitly set field can be marshaled/unmarshaled bytes, err := json.Marshal(obj) @@ -266,28 +844,14 @@ func TestSettersMarkExplicitListUsersRequiredBodyCursorPaginationRequest(t *test // It verifies that setting a field via setter allows successful JSON round-trip }) -} - -func TestSettersListUsersUUIDBodyCursorPaginationRequest(t *testing.T) { - t.Run("SetCursor", func(t *testing.T) { - obj := &ListUsersUUIDBodyCursorPaginationRequest{} - var fernTestValueCursor *uuid.UUID - obj.SetCursor(fernTestValueCursor) - assert.Equal(t, fernTestValueCursor, obj.Cursor) - assert.NotNil(t, obj.explicitFields) - }) - -} - -func TestSettersMarkExplicitListUsersUUIDBodyCursorPaginationRequest(t *testing.T) { - t.Run("SetCursor_MarksExplicit", func(t *testing.T) { + t.Run("SetData_MarksExplicit", func(t *testing.T) { t.Parallel() // Arrange - obj := &ListUsersUUIDBodyCursorPaginationRequest{} - var fernTestValueCursor *uuid.UUID + obj := &ListUsersRequiredAliasCursorResponse{} + var fernTestValueData []*User // Act - obj.SetCursor(fernTestValueCursor) + obj.SetData(fernTestValueData) // Assert - object with explicitly set field can be marshaled/unmarshaled bytes, err := json.Marshal(obj) @@ -976,6 +1540,105 @@ func TestSettersMarkExplicitWithCursor(t *testing.T) { } +func TestJSONMarshalingListUsersAliasCursorResponse(t *testing.T) { + t.Run("MarshalUnmarshal", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersAliasCursorResponse{} + + // Act - Marshal to JSON + data, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed") + assert.NotNil(t, data, "marshaled data should not be nil") + assert.NotEmpty(t, data, "marshaled data should not be empty") + + // Unmarshal back and verify round-trip + var unmarshaled ListUsersAliasCursorResponse + err = json.Unmarshal(data, &unmarshaled) + assert.NoError(t, err, "round-trip unmarshal should succeed") + }) + + t.Run("UnmarshalInvalidJSON", func(t *testing.T) { + t.Parallel() + var obj ListUsersAliasCursorResponse + err := json.Unmarshal([]byte(`{invalid json}`), &obj) + assert.Error(t, err, "unmarshaling invalid JSON should return an error") + }) + + t.Run("UnmarshalEmptyObject", func(t *testing.T) { + t.Parallel() + var obj ListUsersAliasCursorResponse + err := json.Unmarshal([]byte(`{}`), &obj) + assert.NoError(t, err, "unmarshaling empty object should succeed") + }) +} + +func TestJSONMarshalingListUsersNullableAliasCursorResponse(t *testing.T) { + t.Run("MarshalUnmarshal", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersNullableAliasCursorResponse{} + + // Act - Marshal to JSON + data, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed") + assert.NotNil(t, data, "marshaled data should not be nil") + assert.NotEmpty(t, data, "marshaled data should not be empty") + + // Unmarshal back and verify round-trip + var unmarshaled ListUsersNullableAliasCursorResponse + err = json.Unmarshal(data, &unmarshaled) + assert.NoError(t, err, "round-trip unmarshal should succeed") + }) + + t.Run("UnmarshalInvalidJSON", func(t *testing.T) { + t.Parallel() + var obj ListUsersNullableAliasCursorResponse + err := json.Unmarshal([]byte(`{invalid json}`), &obj) + assert.Error(t, err, "unmarshaling invalid JSON should return an error") + }) + + t.Run("UnmarshalEmptyObject", func(t *testing.T) { + t.Parallel() + var obj ListUsersNullableAliasCursorResponse + err := json.Unmarshal([]byte(`{}`), &obj) + assert.NoError(t, err, "unmarshaling empty object should succeed") + }) +} + +func TestJSONMarshalingListUsersRequiredAliasCursorResponse(t *testing.T) { + t.Run("MarshalUnmarshal", func(t *testing.T) { + t.Parallel() + // Arrange + obj := &ListUsersRequiredAliasCursorResponse{} + + // Act - Marshal to JSON + data, err := json.Marshal(obj) + require.NoError(t, err, "marshaling should succeed") + assert.NotNil(t, data, "marshaled data should not be nil") + assert.NotEmpty(t, data, "marshaled data should not be empty") + + // Unmarshal back and verify round-trip + var unmarshaled ListUsersRequiredAliasCursorResponse + err = json.Unmarshal(data, &unmarshaled) + assert.NoError(t, err, "round-trip unmarshal should succeed") + }) + + t.Run("UnmarshalInvalidJSON", func(t *testing.T) { + t.Parallel() + var obj ListUsersRequiredAliasCursorResponse + err := json.Unmarshal([]byte(`{invalid json}`), &obj) + assert.Error(t, err, "unmarshaling invalid JSON should return an error") + }) + + t.Run("UnmarshalEmptyObject", func(t *testing.T) { + t.Parallel() + var obj ListUsersRequiredAliasCursorResponse + err := json.Unmarshal([]byte(`{}`), &obj) + assert.NoError(t, err, "unmarshaling empty object should succeed") + }) +} + func TestJSONMarshalingListUsersRequiredCursorResponse(t *testing.T) { t.Run("MarshalUnmarshal", func(t *testing.T) { t.Parallel() @@ -1141,6 +1804,54 @@ func TestJSONMarshalingWithCursor(t *testing.T) { }) } +func TestStringListUsersAliasCursorResponse(t *testing.T) { + t.Run("StringMethod", func(t *testing.T) { + t.Parallel() + obj := &ListUsersAliasCursorResponse{} + result := obj.String() + assert.NotEmpty(t, result, "String() should return a non-empty representation") + }) + + t.Run("StringMethod_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersAliasCursorResponse + result := obj.String() + assert.Equal(t, "", result, "String() should return for nil receiver") + }) +} + +func TestStringListUsersNullableAliasCursorResponse(t *testing.T) { + t.Run("StringMethod", func(t *testing.T) { + t.Parallel() + obj := &ListUsersNullableAliasCursorResponse{} + result := obj.String() + assert.NotEmpty(t, result, "String() should return a non-empty representation") + }) + + t.Run("StringMethod_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersNullableAliasCursorResponse + result := obj.String() + assert.Equal(t, "", result, "String() should return for nil receiver") + }) +} + +func TestStringListUsersRequiredAliasCursorResponse(t *testing.T) { + t.Run("StringMethod", func(t *testing.T) { + t.Parallel() + obj := &ListUsersRequiredAliasCursorResponse{} + result := obj.String() + assert.NotEmpty(t, result, "String() should return a non-empty representation") + }) + + t.Run("StringMethod_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersRequiredAliasCursorResponse + result := obj.String() + assert.Equal(t, "", result, "String() should return for nil receiver") + }) +} + func TestStringListUsersRequiredCursorResponse(t *testing.T) { t.Run("StringMethod", func(t *testing.T) { t.Parallel() @@ -1221,6 +1932,75 @@ func TestStringWithCursor(t *testing.T) { }) } +func TestExtraPropertiesListUsersAliasCursorResponse(t *testing.T) { + t.Run("GetExtraProperties", func(t *testing.T) { + t.Parallel() + obj := &ListUsersAliasCursorResponse{} + // Should not panic when calling GetExtraProperties() + defer func() { + if r := recover(); r != nil { + t.Errorf("GetExtraProperties() panicked: %v", r) + } + }() + extraProps := obj.GetExtraProperties() + // Result can be nil or an empty/non-empty map + _ = extraProps + }) + + t.Run("GetExtraProperties_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersAliasCursorResponse + extraProps := obj.GetExtraProperties() + assert.Nil(t, extraProps, "nil receiver should return nil without panicking") + }) +} + +func TestExtraPropertiesListUsersNullableAliasCursorResponse(t *testing.T) { + t.Run("GetExtraProperties", func(t *testing.T) { + t.Parallel() + obj := &ListUsersNullableAliasCursorResponse{} + // Should not panic when calling GetExtraProperties() + defer func() { + if r := recover(); r != nil { + t.Errorf("GetExtraProperties() panicked: %v", r) + } + }() + extraProps := obj.GetExtraProperties() + // Result can be nil or an empty/non-empty map + _ = extraProps + }) + + t.Run("GetExtraProperties_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersNullableAliasCursorResponse + extraProps := obj.GetExtraProperties() + assert.Nil(t, extraProps, "nil receiver should return nil without panicking") + }) +} + +func TestExtraPropertiesListUsersRequiredAliasCursorResponse(t *testing.T) { + t.Run("GetExtraProperties", func(t *testing.T) { + t.Parallel() + obj := &ListUsersRequiredAliasCursorResponse{} + // Should not panic when calling GetExtraProperties() + defer func() { + if r := recover(); r != nil { + t.Errorf("GetExtraProperties() panicked: %v", r) + } + }() + extraProps := obj.GetExtraProperties() + // Result can be nil or an empty/non-empty map + _ = extraProps + }) + + t.Run("GetExtraProperties_NilReceiver", func(t *testing.T) { + t.Parallel() + var obj *ListUsersRequiredAliasCursorResponse + extraProps := obj.GetExtraProperties() + assert.Nil(t, extraProps, "nil receiver should return nil without panicking") + }) +} + func TestExtraPropertiesListUsersRequiredCursorResponse(t *testing.T) { t.Run("GetExtraProperties", func(t *testing.T) { t.Parallel() diff --git a/seed/go-sdk/no-retries/retries/raw_client.go b/seed/go-sdk/no-retries/retries/raw_client.go index 9396b87bc59c..a01c669964ca 100644 --- a/seed/go-sdk/no-retries/retries/raw_client.go +++ b/seed/go-sdk/no-retries/retries/raw_client.go @@ -55,7 +55,7 @@ func (r *RawClient) GetUsers( Method: http.MethodGet, Headers: headers, MaxAttempts: options.MaxAttempts, - DisableRetries: options.DisableRetries, + DisableRetries: true, BodyProperties: options.BodyProperties, QueryParameters: options.QueryParameters, Client: options.HTTPClient, diff --git a/seed/go-sdk/oauth-client-credentials/nested/api/nested_api_test/nested_api_test.go b/seed/go-sdk/oauth-client-credentials/nested/api/nested_api_test/nested_api_test.go index 0c97fcc26612..31d67f146afe 100644 --- a/seed/go-sdk/oauth-client-credentials/nested/api/nested_api_test/nested_api_test.go +++ b/seed/go-sdk/oauth-client-credentials/nested/api/nested_api_test/nested_api_test.go @@ -85,6 +85,7 @@ func TestNestedAPIGetSomethingWithWireMock( } client := client.NewClient( option.WithBaseURL(WireMockBaseURL), + option.WithClientCredentials("test_client_id", "test_client_secret"), ) invocationErr := client.Nested.API.GetSomething( context.TODO(), diff --git a/seed/go-sdk/oauth-client-credentials/simple/simple_test/simple_test.go b/seed/go-sdk/oauth-client-credentials/simple/simple_test/simple_test.go index 2d55395319c5..9f79111ab229 100644 --- a/seed/go-sdk/oauth-client-credentials/simple/simple_test/simple_test.go +++ b/seed/go-sdk/oauth-client-credentials/simple/simple_test/simple_test.go @@ -85,6 +85,7 @@ func TestSimpleGetSomethingWithWireMock( } client := client.NewClient( option.WithBaseURL(WireMockBaseURL), + option.WithClientCredentials("test_client_id", "test_client_secret"), ) invocationErr := client.Simple.GetSomething( context.TODO(), diff --git a/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/AsyncRawRetriesClient.java b/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/AsyncRawRetriesClient.java index c5ebef03b5fd..bb64dec12ddb 100644 --- a/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/AsyncRawRetriesClient.java +++ b/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/AsyncRawRetriesClient.java @@ -56,15 +56,10 @@ public CompletableFuture>> getUsers(Request if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } - if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { - okhttpRequest = okhttpRequest - .newBuilder() - .tag( - RetryInterceptor.MaxRetriesOverride.class, - new RetryInterceptor.MaxRetriesOverride( - requestOptions.getMaxRetries().get())) - .build(); - } + okhttpRequest = okhttpRequest + .newBuilder() + .tag(RetryInterceptor.MaxRetriesOverride.class, new RetryInterceptor.MaxRetriesOverride(0)) + .build(); CompletableFuture>> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override diff --git a/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/RawRetriesClient.java b/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/RawRetriesClient.java index a61691789486..1623e6d7b56d 100644 --- a/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/RawRetriesClient.java +++ b/seed/java-sdk/no-retries/src/main/java/com/seed/noRetries/resources/retries/RawRetriesClient.java @@ -52,15 +52,10 @@ public SeedNoRetriesHttpResponse> getUsers(RequestOptions requestOpti if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } - if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { - okhttpRequest = okhttpRequest - .newBuilder() - .tag( - RetryInterceptor.MaxRetriesOverride.class, - new RetryInterceptor.MaxRetriesOverride( - requestOptions.getMaxRetries().get())) - .build(); - } + okhttpRequest = okhttpRequest + .newBuilder() + .tag(RetryInterceptor.MaxRetriesOverride.class, new RetryInterceptor.MaxRetriesOverride(0)) + .build(); try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; diff --git a/seed/php-sdk/no-retries/src/Retries/RetriesClient.php b/seed/php-sdk/no-retries/src/Retries/RetriesClient.php index 215c528c32a0..559e0e708275 100644 --- a/seed/php-sdk/no-retries/src/Retries/RetriesClient.php +++ b/seed/php-sdk/no-retries/src/Retries/RetriesClient.php @@ -70,6 +70,7 @@ public function __construct( public function getUsers(?array $options = null): ?array { $options = array_merge($this->options, $options ?? []); + $options['maxRetries'] = 0; try { $response = $this->client->sendRequest( new JsonApiRequest( diff --git a/seed/python-sdk/seed.yml b/seed/python-sdk/seed.yml index 31705ef101ec..88466f6ba81f 100644 --- a/seed/python-sdk/seed.yml +++ b/seed/python-sdk/seed.yml @@ -453,6 +453,9 @@ fixtures: - customConfig: enable_wire_tests: true outputFolder: with-wire-tests + - customConfig: + stream_abstraction: true + outputFolder: stream-abstraction server-sent-events-openapi: - customConfig: enable_wire_tests: true diff --git a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/base_request.rb b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/base_request.rb index d35df463e5b0..03f6f6042a15 100644 --- a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/base_request.rb +++ b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/base_request.rb @@ -5,7 +5,7 @@ module Internal module Http # @api private class BaseRequest - attr_reader :base_url, :path, :method, :headers, :query, :request_options + attr_reader :base_url, :path, :method, :headers, :query, :request_options, :max_retries # @param base_url [String] The base URL for the request # @param path [String] The path for the request @@ -13,13 +13,15 @@ class BaseRequest # @param headers [Hash] Additional headers for the request (optional) # @param query [Hash] Query parameters for the request (optional) # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {}) + # @param max_retries [Integer, nil] Overrides the client's retry count for this request + def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {}, max_retries: nil) @base_url = base_url @path = path @method = method @headers = headers @query = query @request_options = request_options + @max_retries = max_retries end # @return [Hash] The query parameters merged with additional query parameters from request options. diff --git a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/raw_client.rb b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/raw_client.rb index 4c637e84e4ad..c76cb2df73ca 100644 --- a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/raw_client.rb +++ b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/http/raw_client.rb @@ -67,7 +67,7 @@ def send(request) response = conn.request(http_request) - break unless should_retry?(response, attempt) + break unless should_retry?(response, attempt, max_retries: request.max_retries) delay = retry_delay(response, attempt) sleep(delay) @@ -89,9 +89,11 @@ def protected_header_keys # Determines if a request should be retried based on the response status code. # @param response [Net::HTTPResponse] The HTTP response. # @param attempt [Integer] The current retry attempt (0-indexed). + # @param max_retries [Integer, nil] The request-level retry count, when the endpoint + # overrides the client-level setting. # @return [Boolean] Whether the request should be retried. - def should_retry?(response, attempt) - return false if attempt >= @max_retries + def should_retry?(response, attempt, max_retries: nil) + return false if attempt >= (max_retries || @max_retries) status = response.code.to_i RETRYABLE_STATUSES.include?(status) diff --git a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/json/request.rb b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/json/request.rb index 667ceae8ac59..8fbb4d7874b8 100644 --- a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/json/request.rb +++ b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/json/request.rb @@ -14,8 +14,9 @@ class Request < Seed::Internal::Http::BaseRequest # @param query [Hash] Query parameters for the request (optional) # @param body [Object, nil] The JSON request body (optional) # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) - super(base_url:, path:, method:, headers:, query:, request_options:) + # @param max_retries [Integer, nil] Overrides the client's retry count for this request + def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}, max_retries: nil) + super(base_url:, path:, method:, headers:, query:, request_options:, max_retries:) @body = body end diff --git a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/multipart/multipart_request.rb b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/multipart/multipart_request.rb index 9fa80cee01ab..04297444e268 100644 --- a/seed/ruby-sdk-v2/no-retries/lib/seed/internal/multipart/multipart_request.rb +++ b/seed/ruby-sdk-v2/no-retries/lib/seed/internal/multipart/multipart_request.rb @@ -14,8 +14,9 @@ class Request < Seed::Internal::Http::BaseRequest # @param query [Hash] Query parameters for the request (optional) # @param body [MultipartFormData, nil] The multipart form data for the request (optional) # @param request_options [Seed::RequestOptions, Hash{Symbol=>Object}, nil] - def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}) - super(base_url:, path:, method:, headers:, query:, request_options:) + # @param max_retries [Integer, nil] Overrides the client's retry count for this request + def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {}, max_retries: nil) + super(base_url:, path:, method:, headers:, query:, request_options:, max_retries:) @body = body end diff --git a/seed/ruby-sdk-v2/no-retries/lib/seed/retries/client.rb b/seed/ruby-sdk-v2/no-retries/lib/seed/retries/client.rb index a0d03edbd213..f639690575d1 100644 --- a/seed/ruby-sdk-v2/no-retries/lib/seed/retries/client.rb +++ b/seed/ruby-sdk-v2/no-retries/lib/seed/retries/client.rb @@ -27,7 +27,8 @@ def get_users(request_options: {}, **_params) base_url: request_options[:base_url], method: "GET", path: "/users", - request_options: request_options + request_options: request_options, + max_retries: 0 ) begin response = @client.send(request) diff --git a/seed/rust-sdk/no-retries/src/api/resources/retries/retries.rs b/seed/rust-sdk/no-retries/src/api/resources/retries/retries.rs index 89016b9450ad..74e7da967c14 100644 --- a/seed/rust-sdk/no-retries/src/api/resources/retries/retries.rs +++ b/seed/rust-sdk/no-retries/src/api/resources/retries/retries.rs @@ -28,6 +28,11 @@ impl RetriesClient { /// } /// ``` pub async fn get_users(&self, options: Option) -> Result, ApiError> { + let options = { + let mut o = options.unwrap_or_default(); + o.max_retries = Some(0); + Some(o) + }; self.http_client .execute_request(Method::GET, "/users", None, None, options) .await diff --git a/seed/swift-sdk/accept-header/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/accept-header/Sources/Core/Networking/HTTPClient.swift index b04c9e1c91f4..5e6d1ae4ae87 100644 --- a/seed/swift-sdk/accept-header/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/accept-header/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/alias-extends/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/alias-extends/Sources/Core/Networking/HTTPClient.swift index d2247660af37..f23758f78bb0 100644 --- a/seed/swift-sdk/alias-extends/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/alias-extends/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/alias/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/alias/Sources/Core/Networking/HTTPClient.swift index 236e1925db5e..fa50555a439e 100644 --- a/seed/swift-sdk/alias/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/alias/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/allof-inline/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/allof-inline/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/allof-inline/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/allof-inline/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/allof/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/allof/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/allof/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/allof/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/any-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/any-auth/Sources/Core/Networking/HTTPClient.swift index 76c048e58e8d..a20104ebd86d 100644 --- a/seed/swift-sdk/any-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/any-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/api-wide-base-path-with-default/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/api-wide-base-path-with-default/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/api-wide-base-path-with-default/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/api-wide-base-path-with-default/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/api-wide-base-path/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/api-wide-base-path/Sources/Core/Networking/HTTPClient.swift index 49e388386677..14c552a5c659 100644 --- a/seed/swift-sdk/api-wide-base-path/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/api-wide-base-path/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/audiences/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/audiences/Sources/Core/Networking/HTTPClient.swift index 93cfede2351f..f322c77b684b 100644 --- a/seed/swift-sdk/audiences/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/audiences/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/basic-auth-environment-variables/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/basic-auth-environment-variables/Sources/Core/Networking/HTTPClient.swift index fa8f4087c5cb..1f65143b260d 100644 --- a/seed/swift-sdk/basic-auth-environment-variables/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/basic-auth-environment-variables/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/basic-auth-pw-omitted/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/basic-auth-pw-omitted/Sources/Core/Networking/HTTPClient.swift index ba0d9bcf150e..1047e2ee27d1 100644 --- a/seed/swift-sdk/basic-auth-pw-omitted/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/basic-auth-pw-omitted/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/basic-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/basic-auth/Sources/Core/Networking/HTTPClient.swift index e936104b18f0..f5fef33ff8b8 100644 --- a/seed/swift-sdk/basic-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/basic-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/bearer-token-environment-variable/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/bearer-token-environment-variable/Sources/Core/Networking/HTTPClient.swift index b011729adb2f..71661e4297bd 100644 --- a/seed/swift-sdk/bearer-token-environment-variable/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/bearer-token-environment-variable/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/bytes-download/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/bytes-download/Sources/Core/Networking/HTTPClient.swift index 3f6aca9b80eb..cadf1ac9d6a9 100644 --- a/seed/swift-sdk/bytes-download/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/bytes-download/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/bytes-upload/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/bytes-upload/Sources/Core/Networking/HTTPClient.swift index 5db81f34ce3a..37c3082d8d24 100644 --- a/seed/swift-sdk/bytes-upload/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/bytes-upload/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/circular-references-advanced/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/circular-references-advanced/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/circular-references-advanced/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/circular-references-advanced/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/circular-references-extends/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/circular-references-extends/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/circular-references-extends/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/circular-references-extends/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/circular-references/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/circular-references/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/circular-references/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/circular-references/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/client-side-params/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/client-side-params/no-custom-config/Sources/Core/Networking/HTTPClient.swift index 6c2aab6be396..2c24631dfe4b 100644 --- a/seed/swift-sdk/client-side-params/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/client-side-params/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/client-side-params/with-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/client-side-params/with-custom-config/Sources/Core/Networking/HTTPClient.swift index 7ca809222776..22587c43806a 100644 --- a/seed/swift-sdk/client-side-params/with-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/client-side-params/with-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/content-type/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/content-type/Sources/Core/Networking/HTTPClient.swift index 541e49fb7836..7c3c78b29822 100644 --- a/seed/swift-sdk/content-type/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/content-type/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/cross-package-type-names/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/cross-package-type-names/Sources/Core/Networking/HTTPClient.swift index 06107e1e5451..5f23d2753168 100644 --- a/seed/swift-sdk/cross-package-type-names/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/cross-package-type-names/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/discriminated-union-with-nested-oneof/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/discriminated-union-with-nested-oneof/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/discriminated-union-with-nested-oneof/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/discriminated-union-with-nested-oneof/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/dollar-string-examples/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/dollar-string-examples/Sources/Core/Networking/HTTPClient.swift index 50cd785791f5..abbdcbc1ece9 100644 --- a/seed/swift-sdk/dollar-string-examples/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/dollar-string-examples/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/empty-clients/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/empty-clients/Sources/Core/Networking/HTTPClient.swift index 268171019043..0325ac63ac3a 100644 --- a/seed/swift-sdk/empty-clients/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/empty-clients/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/endpoint-security-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/endpoint-security-auth/Sources/Core/Networking/HTTPClient.swift index aa8db63a04b6..0c59e691c8c3 100644 --- a/seed/swift-sdk/endpoint-security-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/endpoint-security-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/enum/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/enum/Sources/Core/Networking/HTTPClient.swift index 6b11d2b1a700..16c9a1b03270 100644 --- a/seed/swift-sdk/enum/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/enum/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/error-property/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/error-property/Sources/Core/Networking/HTTPClient.swift index 5207537ad613..4448f12b8e69 100644 --- a/seed/swift-sdk/error-property/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/error-property/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/errors/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/errors/Sources/Core/Networking/HTTPClient.swift index 3457604b94a2..54b02c6f0721 100644 --- a/seed/swift-sdk/errors/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/errors/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/examples/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/examples/no-custom-config/Sources/Core/Networking/HTTPClient.swift index eb012dceb1db..5ec9d3e5d9b6 100644 --- a/seed/swift-sdk/examples/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/examples/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/examples/readme-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/examples/readme-config/Sources/Core/Networking/HTTPClient.swift index eb012dceb1db..5ec9d3e5d9b6 100644 --- a/seed/swift-sdk/examples/readme-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/examples/readme-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/exhaustive/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/exhaustive/Sources/Core/Networking/HTTPClient.swift index 50eafe989390..5e136ac70714 100644 --- a/seed/swift-sdk/exhaustive/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/exhaustive/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/extends/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/extends/Sources/Core/Networking/HTTPClient.swift index b6016a9ca58f..85d24d4df3e3 100644 --- a/seed/swift-sdk/extends/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/extends/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/extra-properties/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/extra-properties/Sources/Core/Networking/HTTPClient.swift index 6d6accde19c0..a40d1de65b7d 100644 --- a/seed/swift-sdk/extra-properties/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/extra-properties/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/file-download/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/file-download/Sources/Core/Networking/HTTPClient.swift index 3ad345adcf88..8cd28a1dc4ed 100644 --- a/seed/swift-sdk/file-download/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/file-download/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/file-upload-openapi/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/file-upload-openapi/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/file-upload-openapi/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/file-upload-openapi/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/file-upload/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/file-upload/Sources/Core/Networking/HTTPClient.swift index 962906cf5287..a409b6f19f7d 100644 --- a/seed/swift-sdk/file-upload/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/file-upload/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/folders/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/folders/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/folders/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/folders/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/header-auth-environment-variable/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/header-auth-environment-variable/Sources/Core/Networking/HTTPClient.swift index 2d78af86b9c4..a190af36ffe2 100644 --- a/seed/swift-sdk/header-auth-environment-variable/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/header-auth-environment-variable/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/header-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/header-auth/Sources/Core/Networking/HTTPClient.swift index 7dfeb4c1288f..4224b71d5181 100644 --- a/seed/swift-sdk/header-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/header-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/http-head/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/http-head/Sources/Core/Networking/HTTPClient.swift index 862c2b1eedfe..0afdb4e3949e 100644 --- a/seed/swift-sdk/http-head/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/http-head/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/idempotency-headers/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/idempotency-headers/Sources/Core/Networking/HTTPClient.swift index fd3cb557de49..676b258e398b 100644 --- a/seed/swift-sdk/idempotency-headers/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/idempotency-headers/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/imdb/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/imdb/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/imdb/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/imdb/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/inferred-auth-explicit/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/inferred-auth-explicit/Sources/Core/Networking/HTTPClient.swift index 3aee49e0e602..6cb1bc267ff9 100644 --- a/seed/swift-sdk/inferred-auth-explicit/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/inferred-auth-explicit/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/inferred-auth-implicit-api-key/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/inferred-auth-implicit-api-key/Sources/Core/Networking/HTTPClient.swift index 26be1313174c..4c166c1d5110 100644 --- a/seed/swift-sdk/inferred-auth-implicit-api-key/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/inferred-auth-implicit-api-key/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/inferred-auth-implicit-no-expiry/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/inferred-auth-implicit-no-expiry/Sources/Core/Networking/HTTPClient.swift index b1454df4d52c..d27e10e6634e 100644 --- a/seed/swift-sdk/inferred-auth-implicit-no-expiry/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/inferred-auth-implicit-no-expiry/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/inferred-auth-implicit-reference/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/inferred-auth-implicit-reference/Sources/Core/Networking/HTTPClient.swift index 66c7f38b8a43..78c114d0eae8 100644 --- a/seed/swift-sdk/inferred-auth-implicit-reference/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/inferred-auth-implicit-reference/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/inferred-auth-implicit/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/inferred-auth-implicit/Sources/Core/Networking/HTTPClient.swift index 66c7f38b8a43..78c114d0eae8 100644 --- a/seed/swift-sdk/inferred-auth-implicit/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/inferred-auth-implicit/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/inline-enum-type-name-override/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/inline-enum-type-name-override/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/inline-enum-type-name-override/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/inline-enum-type-name-override/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/license/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/license/Sources/Core/Networking/HTTPClient.swift index 84197b50e9d1..48e5883fed1d 100644 --- a/seed/swift-sdk/license/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/license/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/literal-user-agent/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/literal-user-agent/Sources/Core/Networking/HTTPClient.swift index bd79ba76c459..7080a512bb01 100644 --- a/seed/swift-sdk/literal-user-agent/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/literal-user-agent/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/literal/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/literal/Sources/Core/Networking/HTTPClient.swift index 656a476b48e3..4c79e742219f 100644 --- a/seed/swift-sdk/literal/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/literal/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/literals-unions/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/literals-unions/Sources/Core/Networking/HTTPClient.swift index a3adaf1c6c7e..8d88ed31c796 100644 --- a/seed/swift-sdk/literals-unions/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/literals-unions/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/mixed-case/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/mixed-case/Sources/Core/Networking/HTTPClient.swift index 4a813af8fc4b..086d75b39250 100644 --- a/seed/swift-sdk/mixed-case/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/mixed-case/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/mixed-file-directory/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/mixed-file-directory/Sources/Core/Networking/HTTPClient.swift index 273e2dfff445..44b73048148a 100644 --- a/seed/swift-sdk/mixed-file-directory/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/mixed-file-directory/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/multi-content-type-examples/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/multi-content-type-examples/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/multi-content-type-examples/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/multi-content-type-examples/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/multi-line-docs/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/multi-line-docs/Sources/Core/Networking/HTTPClient.swift index e161ca315849..0208beeffc33 100644 --- a/seed/swift-sdk/multi-line-docs/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/multi-line-docs/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/multi-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/multi-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift index a48b6524f9f4..e625ae5bf22e 100644 --- a/seed/swift-sdk/multi-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/multi-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/multi-url-environment-reference/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/multi-url-environment-reference/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/multi-url-environment-reference/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/multi-url-environment-reference/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/multi-url-environment/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/multi-url-environment/Sources/Core/Networking/HTTPClient.swift index d937fb2f901b..4fea62d82b11 100644 --- a/seed/swift-sdk/multi-url-environment/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/multi-url-environment/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/multiple-request-bodies/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/multiple-request-bodies/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/multiple-request-bodies/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/multiple-request-bodies/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/no-content-response/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/no-content-response/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/no-content-response/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/no-content-response/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/no-environment/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/no-environment/Sources/Core/Networking/HTTPClient.swift index 36356ccd6827..f92a86bee1f7 100644 --- a/seed/swift-sdk/no-environment/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/no-environment/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/no-retries/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/no-retries/Sources/Core/Networking/HTTPClient.swift index d689f8519532..8f04f9ebb627 100644 --- a/seed/swift-sdk/no-retries/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/no-retries/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/no-retries/Sources/Resources/Retries/RetriesClient.swift b/seed/swift-sdk/no-retries/Sources/Resources/Retries/RetriesClient.swift index c3128717b5ee..ff7a2b3deb5d 100644 --- a/seed/swift-sdk/no-retries/Sources/Resources/Retries/RetriesClient.swift +++ b/seed/swift-sdk/no-retries/Sources/Resources/Retries/RetriesClient.swift @@ -26,6 +26,7 @@ public final class RetriesClient: Sendable { method: .get, path: "/users", requestOptions: requestOptions, + retriesDisabled: true, responseType: [User].self ) } diff --git a/seed/swift-sdk/no-retries/Tests/Core/ClientRetriesDisabledTests.swift b/seed/swift-sdk/no-retries/Tests/Core/ClientRetriesDisabledTests.swift new file mode 100644 index 000000000000..29e4b75d68ee --- /dev/null +++ b/seed/swift-sdk/no-retries/Tests/Core/ClientRetriesDisabledTests.swift @@ -0,0 +1,51 @@ +import NoRetries +import Foundation +import Testing + +@Suite("Client Retries Disabled Tests") struct ClientRetriesDisabledTests { + @Test func testDoesNotRetryOn500InternalServerError() async throws { + let stub = HTTPStub() + stub.setResponseSequence([ + (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()), + ( + statusCode: 200, headers: ["Content-Type": "application/json"], + body: Foundation.Data("true".utf8) + ), + ]) + + let client = NoRetriesClient( + baseURL: "https://api.fern.com", + urlSession: stub.urlSession + ) + + do { + _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) + + } catch { + } + try #require(stub.getRequestCount() == 1) + } + + @Test func testIgnoresRequestLevelMaxRetries() async throws { + let stub = HTTPStub() + stub.setResponseSequence([ + (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()), + ( + statusCode: 200, headers: ["Content-Type": "application/json"], + body: Foundation.Data("true".utf8) + ), + ]) + + let client = NoRetriesClient( + baseURL: "https://api.fern.com", + urlSession: stub.urlSession + ) + + do { + _ = try await client.retries.getUsers(requestOptions: RequestOptions(maxRetries: 5, additionalHeaders: stub.headers)) + + } catch { + } + try #require(stub.getRequestCount() == 1) + } +} diff --git a/seed/swift-sdk/no-retries/Tests/Core/ClientRetryTests.swift b/seed/swift-sdk/no-retries/Tests/Core/ClientRetryTests.swift deleted file mode 100644 index 7051d1c5b974..000000000000 --- a/seed/swift-sdk/no-retries/Tests/Core/ClientRetryTests.swift +++ /dev/null @@ -1,355 +0,0 @@ -import NoRetries -import Foundation -import Testing - -@Suite("Client Retry Tests") struct ClientRetryTests { - @Test func testRetryOn408RequestTimeout() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - (statusCode: 408, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - (statusCode: 408, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - try #require(stub.getRequestCount() == 3) - } - - @Test func testRetryOn429TooManyRequests() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - (statusCode: 429, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - (statusCode: 429, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - try #require(stub.getRequestCount() == 3) - } - - @Test func testRetryOn500InternalServerError() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - try #require(stub.getRequestCount() == 3) - } - - @Test func testRetryOn503ServiceUnavailable() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - (statusCode: 503, headers: ["Content-Type": "application/json"], body: Foundation.Data()), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - try #require(stub.getRequestCount() == 2) - } - - @Test func testNoRetryOn400BadRequest() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - ( - statusCode: 400, headers: ["Content-Type": "application/json"], - body: Foundation.Data("{\"errorName\":\"BadRequest\"}".utf8) - ) - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - Issue.record("Expected error to be thrown") - } catch { - try #require(stub.getRequestCount() == 1) - } - } - - @Test func testNoRetryOn404NotFound() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - ( - statusCode: 404, headers: ["Content-Type": "application/json"], - body: Foundation.Data("{\"errorName\":\"NotFound\"}".utf8) - ) - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - Issue.record("Expected error to be thrown") - } catch { - try #require(stub.getRequestCount() == 1) - } - } - - @Test func testMaxRetriesExhausted() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Data()), - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Data()), - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Data()), - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Data()), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - Issue.record("Expected error to be thrown") - } catch { - try #require(stub.getRequestCount() == 3) - } - } - - @Test func testRetryAfterHeaderWithSeconds() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - ( - statusCode: 429, headers: ["Content-Type": "application/json", "Retry-After": "1"], - body: Foundation.Data() - ), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - let startTime = Date() - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - let elapsed = Date().timeIntervalSince(startTime) - - try #require(stub.getRequestCount() == 2) - try #require(elapsed >= 1.0) - } - - @Test func testRetryAfterHeaderWithHTTPDate() async throws { - let stub = HTTPStub() - let futureEpoch = ceil(Date().timeIntervalSince1970) + 1.0 - let futureDate = Date(timeIntervalSince1970: futureEpoch) - let formatter = DateFormatter() - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(abbreviation: "GMT") - formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz" - let httpDate = formatter.string(from: futureDate) - - stub.setResponseSequence([ - ( - statusCode: 429, - headers: ["Content-Type": "application/json", "Retry-After": httpDate], body: Foundation.Data() - ), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - let startTime = Date() - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - let elapsed = Date().timeIntervalSince(startTime) - - try #require(stub.getRequestCount() == 2) - try #require(elapsed >= 0.9) - } - - @Test func testXRateLimitResetHeader() async throws { - let stub = HTTPStub() - let futureTimestamp = Int(ceil(Date().timeIntervalSince1970)) + 1 - - stub.setResponseSequence([ - ( - statusCode: 429, - headers: [ - "Content-Type": "application/json", "X-RateLimit-Reset": "\(futureTimestamp)", - ], body: Foundation.Data() - ), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - let startTime = Date() - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - let elapsed = Date().timeIntervalSince(startTime) - - try #require(stub.getRequestCount() == 2) - try #require(elapsed >= 0.9) - } - - @Test func testEndpointLevelMaxRetriesOverride() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - ( - statusCode: 500, - headers: ["Content-Type": "application/json", "Retry-After": "0.1"], body: Foundation.Data() - ), - ( - statusCode: 500, - headers: ["Content-Type": "application/json", "Retry-After": "0.1"], body: Foundation.Data() - ), - ( - statusCode: 500, - headers: ["Content-Type": "application/json", "Retry-After": "0.1"], body: Foundation.Data() - ), - ( - statusCode: 500, - headers: ["Content-Type": "application/json", "Retry-After": "0.1"], body: Foundation.Data() - ), - ( - statusCode: 500, - headers: ["Content-Type": "application/json", "Retry-After": "0.1"], body: Foundation.Data() - ), - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ), - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(maxRetries: 5, additionalHeaders: stub.headers)) - - } catch { - } - try #require(stub.getRequestCount() == 6) - } - - @Test func testEndpointLevelMaxRetriesZero() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - (statusCode: 500, headers: ["Content-Type": "application/json"], body: Foundation.Data()) - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(maxRetries: 0, additionalHeaders: stub.headers)) - - Issue.record("Expected error to be thrown") - } catch { - try #require(stub.getRequestCount() == 1) - } - } - - @Test func testSuccessOnFirstAttempt() async throws { - let stub = HTTPStub() - stub.setResponseSequence([ - ( - statusCode: 200, headers: ["Content-Type": "application/json"], - body: Foundation.Data("true".utf8) - ) - ]) - - let client = NoRetriesClient( - baseURL: "https://api.fern.com", - urlSession: stub.urlSession - ) - - do { - _ = try await client.retries.getUsers(requestOptions: RequestOptions(additionalHeaders: stub.headers)) - - } catch { - } - try #require(stub.getRequestCount() == 1) - } -} diff --git a/seed/swift-sdk/null-type/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/null-type/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/null-type/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/null-type/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/nullable-allof-extends/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/nullable-allof-extends/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/nullable-allof-extends/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/nullable-allof-extends/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/nullable-optional/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/nullable-optional/no-custom-config/Sources/Core/Networking/HTTPClient.swift index 94221906b45d..0357db4a121f 100644 --- a/seed/swift-sdk/nullable-optional/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/nullable-optional/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/nullable-optional/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/nullable-optional/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift index 94221906b45d..0357db4a121f 100644 --- a/seed/swift-sdk/nullable-optional/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/nullable-optional/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/nullable-request-body/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/nullable-request-body/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/nullable-request-body/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/nullable-request-body/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift index c379dcf5a4fd..ec733723a330 100644 --- a/seed/swift-sdk/nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift index c379dcf5a4fd..ec733723a330 100644 --- a/seed/swift-sdk/nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-custom/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-custom/Sources/Core/Networking/HTTPClient.swift index ce4e8b9565d0..78c09138f401 100644 --- a/seed/swift-sdk/oauth-client-credentials-custom/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-custom/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-default/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-default/Sources/Core/Networking/HTTPClient.swift index 61bfc0d44ee4..cf051b6fbee8 100644 --- a/seed/swift-sdk/oauth-client-credentials-default/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-default/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-environment-variables/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-environment-variables/Sources/Core/Networking/HTTPClient.swift index 34a0291a737a..3c96b1ef472d 100644 --- a/seed/swift-sdk/oauth-client-credentials-environment-variables/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-environment-variables/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-mandatory-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-mandatory-auth/Sources/Core/Networking/HTTPClient.swift index 7905236fbeb4..80de182c2b0b 100644 --- a/seed/swift-sdk/oauth-client-credentials-mandatory-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-mandatory-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-nested-root/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-nested-root/Sources/Core/Networking/HTTPClient.swift index ce4e8b9565d0..78c09138f401 100644 --- a/seed/swift-sdk/oauth-client-credentials-nested-root/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-nested-root/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-openapi/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-openapi/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/oauth-client-credentials-openapi/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-openapi/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-reference/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-reference/Sources/Core/Networking/HTTPClient.swift index bb30570b435a..092ea7fbcba7 100644 --- a/seed/swift-sdk/oauth-client-credentials-reference/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-reference/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials-with-variables/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials-with-variables/Sources/Core/Networking/HTTPClient.swift index 67a74dc4eb8a..232ff158e693 100644 --- a/seed/swift-sdk/oauth-client-credentials-with-variables/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials-with-variables/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-client-credentials/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-client-credentials/Sources/Core/Networking/HTTPClient.swift index ce4e8b9565d0..78c09138f401 100644 --- a/seed/swift-sdk/oauth-client-credentials/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-client-credentials/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/oauth-pkce/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/oauth-pkce/Sources/Core/Networking/HTTPClient.swift index 82fbd9e53ec0..e5e35ce60477 100644 --- a/seed/swift-sdk/oauth-pkce/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/oauth-pkce/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/object/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/object/Sources/Core/Networking/HTTPClient.swift index c2726c22c021..fd0fbedd2635 100644 --- a/seed/swift-sdk/object/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/object/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/objects-with-imports/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/objects-with-imports/Sources/Core/Networking/HTTPClient.swift index 4acab5c93ebc..5cc96e930427 100644 --- a/seed/swift-sdk/objects-with-imports/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/objects-with-imports/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/openapi-path-param-body-collision/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/openapi-path-param-body-collision/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/openapi-path-param-body-collision/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/openapi-path-param-body-collision/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/openapi-request-body-ref/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/openapi-request-body-ref/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/openapi-request-body-ref/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/openapi-request-body-ref/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/openapi-subtitle/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/openapi-subtitle/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/openapi-subtitle/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/openapi-subtitle/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/optional/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/optional/Sources/Core/Networking/HTTPClient.swift index 4acab5c93ebc..5cc96e930427 100644 --- a/seed/swift-sdk/optional/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/optional/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/package-yml/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/package-yml/Sources/Core/Networking/HTTPClient.swift index 7dac317fe73e..e0987965d943 100644 --- a/seed/swift-sdk/package-yml/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/package-yml/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/pagination-custom/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/pagination-custom/Sources/Core/Networking/HTTPClient.swift index e85bd8595bfe..86967375d902 100644 --- a/seed/swift-sdk/pagination-custom/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/pagination-custom/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/pagination-uri-path/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/pagination-uri-path/Sources/Core/Networking/HTTPClient.swift index 9cc4ccbae17d..44fdb24c7a89 100644 --- a/seed/swift-sdk/pagination-uri-path/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/pagination-uri-path/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Core/Networking/HTTPClient.swift index e85bd8595bfe..86967375d902 100644 --- a/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/pagination/custom-pager-with-exception-handler/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/pagination/custom-pager/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/pagination/custom-pager/Sources/Core/Networking/HTTPClient.swift index e85bd8595bfe..86967375d902 100644 --- a/seed/swift-sdk/pagination/custom-pager/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/pagination/custom-pager/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/pagination/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/pagination/no-custom-config/Sources/Core/Networking/HTTPClient.swift index e85bd8595bfe..86967375d902 100644 --- a/seed/swift-sdk/pagination/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/pagination/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/path-parameters/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/path-parameters/Sources/Core/Networking/HTTPClient.swift index a652b3174030..642da7327c1c 100644 --- a/seed/swift-sdk/path-parameters/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/path-parameters/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/plain-text/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/plain-text/Sources/Core/Networking/HTTPClient.swift index bc7a6f8ed4c8..a9a8bf13b08b 100644 --- a/seed/swift-sdk/plain-text/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/plain-text/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/property-access/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/property-access/Sources/Core/Networking/HTTPClient.swift index 00706fa3a075..a6fecc24e0f7 100644 --- a/seed/swift-sdk/property-access/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/property-access/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/public-object/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/public-object/Sources/Core/Networking/HTTPClient.swift index ca0d7d47527d..d2eb7343560b 100644 --- a/seed/swift-sdk/public-object/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/public-object/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/query-param-name-conflict/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/query-param-name-conflict/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/query-param-name-conflict/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/query-param-name-conflict/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/query-parameters-openapi-as-objects/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/query-parameters-openapi-as-objects/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/query-parameters-openapi-as-objects/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/query-parameters-openapi-as-objects/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/query-parameters-openapi/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/query-parameters-openapi/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/query-parameters-openapi/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/query-parameters-openapi/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/query-parameters/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/query-parameters/Sources/Core/Networking/HTTPClient.swift index 1e140a437ac0..91118b5ffc33 100644 --- a/seed/swift-sdk/query-parameters/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/query-parameters/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/request-parameters/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/request-parameters/Sources/Core/Networking/HTTPClient.swift index 734a65221ea7..f7885ddd0feb 100644 --- a/seed/swift-sdk/request-parameters/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/request-parameters/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/required-nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/required-nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/required-nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/required-nullable/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/required-nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/required-nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/required-nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/required-nullable/nullable-as-optional/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/reserved-keywords/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/reserved-keywords/Sources/Core/Networking/HTTPClient.swift index b1fb0d382b15..ea65cef8ddf4 100644 --- a/seed/swift-sdk/reserved-keywords/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/reserved-keywords/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/respect-optional-request-body/Sources/ApiClient.swift b/seed/swift-sdk/respect-optional-request-body/Sources/ApiClient.swift index 737b9320f10a..cc1cae7df8b9 100644 --- a/seed/swift-sdk/respect-optional-request-body/Sources/ApiClient.swift +++ b/seed/swift-sdk/respect-optional-request-body/Sources/ApiClient.swift @@ -63,7 +63,7 @@ public final class ApiClient: Sendable { /// _ = try await client.refund( /// id: "refund-id", /// request: RefundRequest( - /// amount: 60 + /// /// ) /// ) /// } diff --git a/seed/swift-sdk/respect-optional-request-body/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/respect-optional-request-body/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/respect-optional-request-body/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/respect-optional-request-body/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientErrorTests.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientErrorTests.swift index 445837ff38a0..9e27549d3031 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientErrorTests.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientErrorTests.swift @@ -22,7 +22,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -58,7 +58,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -94,7 +94,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -132,7 +132,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -168,7 +168,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -206,7 +206,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -242,7 +242,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientRetryTests.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientRetryTests.swift index 519a3b71bc97..3c60777b74cb 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientRetryTests.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Core/ClientRetryTests.swift @@ -23,7 +23,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -53,7 +53,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -83,7 +83,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -112,7 +112,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -140,7 +140,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -169,7 +169,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -198,7 +198,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -232,7 +232,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -276,7 +276,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -316,7 +316,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) @@ -367,7 +367,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(maxRetries: 5, additionalHeaders: stub.headers) ) @@ -392,7 +392,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(maxRetries: 0, additionalHeaders: stub.headers) ) @@ -421,7 +421,7 @@ import Testing _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ), requestOptions: RequestOptions(additionalHeaders: stub.headers) ) diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example0.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example0.swift index 64bcdd58fb0f..b0a3761d5a13 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example0.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example0.swift @@ -8,7 +8,7 @@ enum Example0 { _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ) ) } diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example1.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example1.swift index b00af1462c28..695cde67fe92 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example1.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example1.swift @@ -6,9 +6,9 @@ enum Example1 { let client = ApiClient(baseURL: "https://api.fern.com") _ = try await client.refund( - id: "id", + id: "refund-id", request: RefundRequest( - amount: 1.1 + amount: 60 ) ) } diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example2.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example2.swift index 3a6c280e05b2..37efd1fc476a 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example2.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example2.swift @@ -5,10 +5,10 @@ enum Example2 { static func snippet() async throws { let client = ApiClient(baseURL: "https://api.fern.com") - _ = try await client.requiredRefund( - id: "refund-id", + _ = try await client.refund( + id: "id", request: RefundRequest( - amount: 60 + amount: 1.1 ) ) } diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example3.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example3.swift index 9304dd2602a4..d5405408b894 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example3.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example3.swift @@ -6,9 +6,9 @@ enum Example3 { let client = ApiClient(baseURL: "https://api.fern.com") _ = try await client.requiredRefund( - id: "id", + id: "refund-id", request: RefundRequest( - amount: 1.1 + amount: 60 ) ) } diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example4.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example4.swift index 323c04ade478..b2950c3f5516 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example4.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example4.swift @@ -5,8 +5,11 @@ enum Example4 { static func snippet() async throws { let client = ApiClient(baseURL: "https://api.fern.com") - _ = try await client.bulkRefund(request: RefundRequest( - - )) + _ = try await client.requiredRefund( + id: "id", + request: RefundRequest( + amount: 1.1 + ) + ) } } diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example5.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example5.swift index 9d062ae447c9..1b88b16a0193 100644 --- a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example5.swift +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example5.swift @@ -6,7 +6,7 @@ enum Example5 { let client = ApiClient(baseURL: "https://api.fern.com") _ = try await client.bulkRefund(request: RefundRequest( - amount: 1.1 + )) } } diff --git a/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example6.swift b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example6.swift new file mode 100644 index 000000000000..95e09ab5550d --- /dev/null +++ b/seed/swift-sdk/respect-optional-request-body/Tests/Snippets/Example6.swift @@ -0,0 +1,12 @@ +import Foundation +import Api + +enum Example6 { + static func snippet() async throws { + let client = ApiClient(baseURL: "https://api.fern.com") + + _ = try await client.bulkRefund(request: RefundRequest( + amount: 1.1 + )) + } +} diff --git a/seed/swift-sdk/respect-optional-request-body/reference.md b/seed/swift-sdk/respect-optional-request-body/reference.md index 69ec54ff0fd7..863a6be7d13b 100644 --- a/seed/swift-sdk/respect-optional-request-body/reference.md +++ b/seed/swift-sdk/respect-optional-request-body/reference.md @@ -21,7 +21,7 @@ private func main() async throws { _ = try await client.refund( id: "refund-id", request: RefundRequest( - amount: 60 + ) ) } diff --git a/seed/swift-sdk/response-property/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/response-property/Sources/Core/Networking/HTTPClient.swift index a411dbb991f6..3f005191e9d6 100644 --- a/seed/swift-sdk/response-property/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/response-property/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/schemaless-request-body-examples/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/schemaless-request-body-examples/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/schemaless-request-body-examples/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/schemaless-request-body-examples/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/server-sent-event-examples/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/server-sent-event-examples/Sources/Core/Networking/HTTPClient.swift index 2a025f9d980e..2d2e89b98b53 100644 --- a/seed/swift-sdk/server-sent-event-examples/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/server-sent-event-examples/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/server-sent-events-openapi/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/server-sent-events-openapi/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/server-sent-events-openapi/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/server-sent-events-openapi/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/server-sent-events-resumable/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/server-sent-events-resumable/Sources/Core/Networking/HTTPClient.swift index b177f343802c..9463371c1468 100644 --- a/seed/swift-sdk/server-sent-events-resumable/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/server-sent-events-resumable/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/server-sent-events/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/server-sent-events/Sources/Core/Networking/HTTPClient.swift index 2a025f9d980e..2d2e89b98b53 100644 --- a/seed/swift-sdk/server-sent-events/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/server-sent-events/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/server-url-templating-single-url/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/server-url-templating-single-url/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/server-url-templating-single-url/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/server-url-templating-single-url/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/server-url-templating/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/server-url-templating/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/server-url-templating/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/server-url-templating/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/simple-api/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/simple-api/Sources/Core/Networking/HTTPClient.swift index fece38737d4a..d75e1c603d17 100644 --- a/seed/swift-sdk/simple-api/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/simple-api/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/simple-fhir/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/simple-fhir/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/simple-fhir/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/simple-fhir/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/single-url-environment-default/no-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/single-url-environment-default/no-custom-config/Sources/Core/Networking/HTTPClient.swift index 1ea2685d15c4..c7ad6fb06fd7 100644 --- a/seed/swift-sdk/single-url-environment-default/no-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/single-url-environment-default/no-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/single-url-environment-default/with-custom-config/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/single-url-environment-default/with-custom-config/Sources/Core/Networking/HTTPClient.swift index 1ea2685d15c4..c7ad6fb06fd7 100644 --- a/seed/swift-sdk/single-url-environment-default/with-custom-config/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/single-url-environment-default/with-custom-config/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/single-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/single-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift index f930ad44a114..de32a5ba4084 100644 --- a/seed/swift-sdk/single-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/single-url-environment-no-default/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/streaming-parameter/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/streaming-parameter/Sources/Core/Networking/HTTPClient.swift index a85893dd844f..59dd7c0b2688 100644 --- a/seed/swift-sdk/streaming-parameter/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/streaming-parameter/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/streaming/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/streaming/Sources/Core/Networking/HTTPClient.swift index a85893dd844f..59dd7c0b2688 100644 --- a/seed/swift-sdk/streaming/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/streaming/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/trace/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/trace/Sources/Core/Networking/HTTPClient.swift index cd59ef970a78..6095473341c0 100644 --- a/seed/swift-sdk/trace/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/trace/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/undiscriminated-union-with-response-property/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/undiscriminated-union-with-response-property/Sources/Core/Networking/HTTPClient.swift index 2bd6888faa3b..d3b9eeb97ba4 100644 --- a/seed/swift-sdk/undiscriminated-union-with-response-property/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/undiscriminated-union-with-response-property/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/undiscriminated-unions/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/undiscriminated-unions/Sources/Core/Networking/HTTPClient.swift index 91ecb203b81b..0233d11246b0 100644 --- a/seed/swift-sdk/undiscriminated-unions/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/undiscriminated-unions/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/union-query-parameters/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/union-query-parameters/Sources/Core/Networking/HTTPClient.swift index 9e41532caacf..6fd8b0867226 100644 --- a/seed/swift-sdk/union-query-parameters/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/union-query-parameters/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/unions-with-local-date/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/unions-with-local-date/Sources/Core/Networking/HTTPClient.swift index 8cbf5b80a431..6c8467d49c99 100644 --- a/seed/swift-sdk/unions-with-local-date/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/unions-with-local-date/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/unions/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/unions/Sources/Core/Networking/HTTPClient.swift index 8cbf5b80a431..6c8467d49c99 100644 --- a/seed/swift-sdk/unions/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/unions/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/unknown/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/unknown/Sources/Core/Networking/HTTPClient.swift index ad08b3fc1f6e..6755d40bdb5d 100644 --- a/seed/swift-sdk/unknown/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/unknown/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/url-form-encoded/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/url-form-encoded/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/url-form-encoded/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/url-form-encoded/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/validation/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/validation/Sources/Core/Networking/HTTPClient.swift index 10ed1f46aef6..7817ab1c03ff 100644 --- a/seed/swift-sdk/validation/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/validation/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/variables/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/variables/Sources/Core/Networking/HTTPClient.swift index fae4b06d2b32..0bd980e71582 100644 --- a/seed/swift-sdk/variables/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/variables/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/version-no-default/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/version-no-default/Sources/Core/Networking/HTTPClient.swift index 843cdd2e2942..9084c90a65ba 100644 --- a/seed/swift-sdk/version-no-default/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/version-no-default/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/version/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/version/Sources/Core/Networking/HTTPClient.swift index 843cdd2e2942..9084c90a65ba 100644 --- a/seed/swift-sdk/version/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/version/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/webhook-audience/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/webhook-audience/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/webhook-audience/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/webhook-audience/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/webhooks/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/webhooks/Sources/Core/Networking/HTTPClient.swift index d6cd8283e862..8619eefbc063 100644 --- a/seed/swift-sdk/webhooks/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/webhooks/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/websocket-bearer-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/websocket-bearer-auth/Sources/Core/Networking/HTTPClient.swift index 8fea893695eb..0b155a5d2e18 100644 --- a/seed/swift-sdk/websocket-bearer-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/websocket-bearer-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/websocket-inferred-auth/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/websocket-inferred-auth/Sources/Core/Networking/HTTPClient.swift index d8f0452b76ef..1c4a707ef29f 100644 --- a/seed/swift-sdk/websocket-inferred-auth/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/websocket-inferred-auth/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/websocket-multi-url/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/websocket-multi-url/Sources/Core/Networking/HTTPClient.swift index 6321f8e54bc7..c622c85f3d4f 100644 --- a/seed/swift-sdk/websocket-multi-url/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/websocket-multi-url/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/websocket/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/websocket/Sources/Core/Networking/HTTPClient.swift index bd660bf39b21..e24079341384 100644 --- a/seed/swift-sdk/websocket/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/websocket/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/x-fern-default/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/x-fern-default/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/x-fern-default/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/x-fern-default/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/swift-sdk/x-fern-global-parameters/Sources/Core/Networking/HTTPClient.swift b/seed/swift-sdk/x-fern-global-parameters/Sources/Core/Networking/HTTPClient.swift index 5052f16706e1..18c3d7ca8caa 100644 --- a/seed/swift-sdk/x-fern-global-parameters/Sources/Core/Networking/HTTPClient.swift +++ b/seed/swift-sdk/x-fern-global-parameters/Sources/Core/Networking/HTTPClient.swift @@ -21,7 +21,8 @@ final class HTTPClient: Swift.Sendable { headers requestHeaders: [Swift.String: Swift.String?] = [:], queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws { _ = try await performRequest( method: method, @@ -31,6 +32,7 @@ final class HTTPClient: Swift.Sendable { queryParams: requestQueryParams, body: requestBody, requestOptions: requestOptions, + retriesDisabled: retriesDisabled, responseType: Foundation.Data.self ) } @@ -44,6 +46,7 @@ final class HTTPClient: Swift.Sendable { queryParams requestQueryParams: [Swift.String: QueryParameter?] = [:], body requestBody: Any? = nil, requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false, responseType: T.Type ) async throws -> T { let requestBody: HTTP.RequestBody? = requestBody.map { body in @@ -70,7 +73,8 @@ final class HTTPClient: Swift.Sendable { let (data, _) = try await executeRequestWithURLSession( request, - requestOptions: requestOptions + requestOptions: requestOptions, + retriesDisabled: retriesDisabled ) if responseType == Foundation.Data.self { @@ -265,9 +269,12 @@ final class HTTPClient: Swift.Sendable { private func executeRequestWithURLSession( _ request: Networking.URLRequest, - requestOptions: RequestOptions? = nil + requestOptions: RequestOptions? = nil, + retriesDisabled: Swift.Bool = false ) async throws -> (Foundation.Data, Swift.String?) { - let maxRetries = requestOptions?.maxRetries ?? clientConfig.maxRetries + // Endpoints declaring `retries: { disabled: true }` never retry, taking precedence over both + // the client-level and the per-request retry configuration. + let maxRetries = retriesDisabled ? 0 : (requestOptions?.maxRetries ?? clientConfig.maxRetries) var lastResponse: (Foundation.Data, Networking.HTTPURLResponse)? for attempt in 0...maxRetries { diff --git a/seed/ts-sdk/no-retries/src/api/resources/retries/client/Client.ts b/seed/ts-sdk/no-retries/src/api/resources/retries/client/Client.ts index 6a821bcab1f6..176087b24c06 100644 --- a/seed/ts-sdk/no-retries/src/api/resources/retries/client/Client.ts +++ b/seed/ts-sdk/no-retries/src/api/resources/retries/client/Client.ts @@ -48,7 +48,7 @@ export class RetriesClient { headers: _headers, queryString: core.url.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(), timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000, - maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries, + maxRetries: 0, abortSignal: requestOptions?.abortSignal, fetchFn: this._options?.fetch, logging: this._options.logging, diff --git a/test-definitions/fern/apis/go-request-body-pagination/definition/users.yml b/test-definitions/fern/apis/go-request-body-pagination/definition/users.yml index 79847cf4c912..ae82aa519110 100644 --- a/test-definitions/fern/apis/go-request-body-pagination/definition/users.yml +++ b/test-definitions/fern/apis/go-request-body-pagination/definition/users.yml @@ -19,6 +19,33 @@ types: next_cursor: optional data: list + Cursor: + docs: A named alias that resolves to an optional string, i.e. a pointer in Go. + type: optional + + RequiredCursor: + docs: A named alias that resolves to a required string, i.e. a value in Go. + type: string + + NullableCursor: + docs: A named alias that resolves to a nullable string, which is also a pointer in Go. + type: nullable + + ListUsersAliasCursorResponse: + properties: + next_cursor: Cursor + data: list + + ListUsersRequiredAliasCursorResponse: + properties: + next_cursor: RequiredCursor + data: list + + ListUsersNullableAliasCursorResponse: + properties: + next_cursor: NullableCursor + data: list + WithCursor: properties: cursor: optional @@ -77,6 +104,57 @@ service: cursor: optional response: ListUsersUuidCursorResponse + listWithAliasBodyCursorPagination: + docs: | + Pagination endpoint whose cursor is a named alias resolving to an optional string. The alias + generates as a pointer in Go, so an empty string cursor terminates the pager here too. + pagination: + cursor: $request.cursor + next_cursor: $response.next_cursor + results: $response.data + method: POST + path: /alias-cursor + request: + name: ListUsersAliasBodyCursorPaginationRequest + body: + properties: + cursor: Cursor + response: ListUsersAliasCursorResponse + + listWithRequiredAliasBodyCursorPagination: + docs: | + Pagination endpoint whose cursor is a named alias resolving to a required string. The alias + generates as a value in Go, where the zero value is already the empty string. + pagination: + cursor: $request.cursor + next_cursor: $response.next_cursor + results: $response.data + method: POST + path: /required-alias-cursor + request: + name: ListUsersRequiredAliasBodyCursorPaginationRequest + body: + properties: + cursor: RequiredCursor + response: ListUsersRequiredAliasCursorResponse + + listWithNullableAliasBodyCursorPagination: + docs: | + Pagination endpoint whose cursor is a named alias resolving to a nullable string. Nullable + generates as a pointer in Go, so an empty string cursor terminates the pager here too. + pagination: + cursor: $request.cursor + next_cursor: $response.next_cursor + results: $response.data + method: POST + path: /nullable-alias-cursor + request: + name: ListUsersNullableAliasBodyCursorPaginationRequest + body: + properties: + cursor: NullableCursor + response: ListUsersNullableAliasCursorResponse + listWithBodyOffsetPagination: docs: Pagination endpoint with a top-level page field in the request body. pagination: