Skip to content

feat: auto-generate harmonised, ergonomic SDKs across 12 languages#35

Draft
mridang wants to merge 771 commits into
masterfrom
feat/better-generated-clients
Draft

feat: auto-generate harmonised, ergonomic SDKs across 12 languages#35
mridang wants to merge 771 commits into
masterfrom
feat/better-generated-clients

Conversation

@mridang

@mridang mridang commented Feb 2, 2026

Copy link
Copy Markdown
Owner

Description

Adds 7 new language generators (Kotlin, Go, Rust, Dart, Swift, Elixir, C#) alongside the existing 5 (Java, Python, Ruby, PHP, Node/TypeScript) to produce fully harmonised, ergonomic client SDKs from any OpenAPI 3.0 spec. Every generated SDK shares the same architecture: BaseApi, DefaultApiClient, ObjectSerializer, ValueSerializer, HeaderSelector, Configuration, structured exceptions, per-operation Options classes, authentication (API key, Basic, Bearer, OAuth2), OpenTelemetry propagation, HTTP compression, TLS/proxy transport options, binary upload/download, and per-operation server overrides.

Motivation and Context

The default OpenAPI Generator templates produce inconsistent, non-idiomatic code across languages. This PR replaces them with a unified set of Mustache templates that generate production-grade SDKs on par with hand-written clients from Stripe, Stainless, or Speakeasy — fully linted, statically analysed, formatted, and integration-tested in Docker.

How Has This Been Tested?

All 12 SDKs are generated on-the-fly during the Maven build. Each SDK runs a full test suite inside Docker covering unit tests, integration tests against a live Petstore mock, formatting checks, linting, and static analysis. Regression tests for HeaderSelector and ValueSerializer ensure cross-language parity.

Checklist

  • I have checked my code for any possible security vulnerabilities

@gitguardian

gitguardian Bot commented Mar 5, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@mridang mridang force-pushed the feat/better-generated-clients branch from 5166131 to 3cb26d3 Compare March 10, 2026 00:23
@mridang mridang force-pushed the feat/better-generated-clients branch from 1113be3 to bc8fd3d Compare April 13, 2026 06:21
@mridang mridang self-assigned this Apr 16, 2026
@mridang mridang marked this pull request as draft April 21, 2026 06:11
@mridang mridang force-pushed the feat/better-generated-clients branch 9 times, most recently from e134b80 to f61e4ed Compare April 27, 2026 08:19
@mridang mridang changed the title feat: added tests to enure that we iterate rapidly feat: auto-generate harmonised, ergonomic SDKs across 12 languages Apr 28, 2026
@mridang mridang force-pushed the feat/better-generated-clients branch from ace5c59 to 9039e83 Compare April 28, 2026 09:21
mridang added a commit that referenced this pull request May 4, 2026
…, #35)

- #2: Make Go ApiResult.Data nullable (*T pointer type)
- #5: Dart caCertPath defaults to null instead of empty string
- #9: Add toCookieValue to ObjectSerializer in all 12 languages
- #14: Dart DefaultApiClient uses caCertPath with SecurityContext
- #15: Fix Dart redirect handling (autoRedirect + maxRedirects)
- #18: Route Dart/Rust path params through ValueSerializer.serializeStyled()
- #26: Add default value support in Go, Rust, Swift, Dart, Elixir models
- #35: Update Go version to 1.26

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mridang mridang force-pushed the feat/better-generated-clients branch 2 times, most recently from 58765c7 to df347a4 Compare May 4, 2026 19:34
mridang added a commit that referenced this pull request May 4, 2026
…, #35)

- #2: Make Go ApiResult.Data nullable (*T pointer type)
- #5: Dart caCertPath defaults to null instead of empty string
- #9: Add toCookieValue to ObjectSerializer in all 12 languages
- #14: Dart DefaultApiClient uses caCertPath with SecurityContext
- #15: Fix Dart redirect handling (autoRedirect + maxRedirects)
- #18: Route Dart/Rust path params through ValueSerializer.serializeStyled()
- #26: Add default value support in Go, Rust, Swift, Dart, Elixir models
- #35: Update Go version to 1.26

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mridang mridang force-pushed the feat/better-generated-clients branch from df347a4 to 642ef91 Compare May 4, 2026 20:27
mridang added a commit that referenced this pull request May 4, 2026
…, #35)

- #2: Make Go ApiResult.Data nullable (*T pointer type)
- #5: Dart caCertPath defaults to null instead of empty string
- #9: Add toCookieValue to ObjectSerializer in all 12 languages
- #14: Dart DefaultApiClient uses caCertPath with SecurityContext
- #15: Fix Dart redirect handling (autoRedirect + maxRedirects)
- #18: Route Dart/Rust path params through ValueSerializer.serializeStyled()
- #26: Add default value support in Go, Rust, Swift, Dart, Elixir models
- #35: Update Go version to 1.26

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mridang mridang force-pushed the feat/better-generated-clients branch from 642ef91 to 6713b61 Compare May 5, 2026 06:35
mridang added a commit that referenced this pull request May 5, 2026
…, #35)

- #2: Make Go ApiResult.Data nullable (*T pointer type)
- #5: Dart caCertPath defaults to null instead of empty string
- #9: Add toCookieValue to ObjectSerializer in all 12 languages
- #14: Dart DefaultApiClient uses caCertPath with SecurityContext
- #15: Fix Dart redirect handling (autoRedirect + maxRedirects)
- #18: Route Dart/Rust path params through ValueSerializer.serializeStyled()
- #26: Add default value support in Go, Rust, Swift, Dart, Elixir models
- #35: Update Go version to 1.26

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mridang mridang force-pushed the feat/better-generated-clients branch from 6713b61 to f6d73f3 Compare May 5, 2026 07:36
mridang added a commit that referenced this pull request May 5, 2026
…, #35)

- #2: Make Go ApiResult.Data nullable (*T pointer type)
- #5: Dart caCertPath defaults to null instead of empty string
- #9: Add toCookieValue to ObjectSerializer in all 12 languages
- #14: Dart DefaultApiClient uses caCertPath with SecurityContext
- #15: Fix Dart redirect handling (autoRedirect + maxRedirects)
- #18: Route Dart/Rust path params through ValueSerializer.serializeStyled()
- #26: Add default value support in Go, Rust, Swift, Dart, Elixir models
- #35: Update Go version to 1.26

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mridang mridang force-pushed the feat/better-generated-clients branch 4 times, most recently from b4d5152 to 1c15042 Compare May 10, 2026 11:55
mridang added 30 commits June 14, 2026 16:51
conn is provably non-nil under the respond_to?(:close) guard, so the
safe-navigation operator is redundant (Qodana RubyRedundantSafeNavigation).
Matches the existing non-safe-navigation close on response bodies.
Harmonize base-class construction with java/kotlin/csharp/php, where the
abstract base API constructor is already protected. node's BaseApi ctor
becomes protected (concrete *Api classes keep their own public ctor);
swift's BaseApi init becomes internal and each concrete *Api gains a
public override init delegating to super. Consumers still construct the
concrete classes; the base is no longer publicly instantiable.
php: drop unnecessary curly braces in string interpolation, redundant
constructor parentheses, and a useless parenthesis group. ruby: use %w
for plain string-literal arrays in tests. python: restructure
_sanitize_for_serialization to a single return so all paths return a
value. No behaviour change; verified by the php/ruby/python build, lint,
static-analysis and client specs.
The facade (clientClassName) now exposes a static withAuthenticator(Authenticator)
entry point so the whole class can be generated; bespoke authenticators are passed
in rather than requiring a hand-written facade per SDK.
When clientClassName equals the root module segment (e.g. Zitadel inside
module Zitadel::Client), an unanchored reference like Zitadel::Client::X
resolves to the facade class and NameErrors. Anchor production references
with a leading :: and make the facade RBS honour clientClassName.
Node derived its default User-Agent from the hardcoded placeholder
"openapi-typescript-client" instead of the npm package name, and Go and
Rust left the User-Agent unset (nil/None) by default. The other nine SDKs
already inject "<pkg>/<ver> (<lang>)".

Node now derives the default from npmName, and Go and Rust inject the same
branded <pkg>/<ver> (<lang>) default that every other SDK uses. The Go and
Rust default-User-Agent tests assert the branded value instead of nil/None.
Emit google.protobuf.Duration "<seconds>s" form (e.g. "3600s") which
Zitadel's API requires; ISO-8601 ("PT1H") was rejected as HTTP 400.
Mask token / password / client secret / api key / implicit access token as
*** in the default string, debug, repr and inspect representations of every
generated authenticator, across all 12 languages. Previously only php, ruby,
node and elixir masked comprehensively; java, python, csharp, rust, kotlin,
swift, dart and go leaked secrets through toString / Debug / __repr__.

Because the masking now lives in the templates, regeneration re-adds it, so
the generated BearerAuthenticator and scheme-named authenticators stay masked
and idempotent instead of being reverted on every run. Redaction assertions
were added to the generated authenticator test templates so they survive the
prune step.
The redaction assertions belong in each authenticator's own test file in the
consuming SDKs, not sprinkled across the generated per-authenticator test
templates under ad-hoc method names. Remove them from the templates and
regenerate the goldens; the secret masking itself stays in the authenticator
templates.
Each per-authenticator test template now carries one consistently-named
redaction test asserting the secret is masked as *** in the authenticator's
default string representation, mirroring the per-authenticator tests in the
consuming SDKs. Also make the Java BasicAuthenticator mask uniform (*** instead
of <redacted>) so the marker matches every other authenticator.
…assertion

The protobuf-duration migration left three test templates referencing the
removed ISO-8601 API: the Swift ApiErrorTests used ISO8601DurationError and the
Dart object_serializer_test used parseIso8601Duration / formatIso8601Duration /
Iso8601DurationFormatException. Point them at the protobuf-duration API and
rewrite the Dart duration cases to protobuf-JSON literals (e.g. "5400s").

Elixir redacts secrets by omission via @derive {Inspect, except: ...} rather
than a *** marker, so its redaction tests now assert the secret value is absent
instead of asserting a *** placeholder.
The protobuf-duration migration left ISO-8601 duration assertions in the Swift,
Dart and Elixir test templates (the serializer emits "5400s" / "0s" / "-300s",
not "PT1H30M"). Rewrite every duration test in those languages to protobuf-JSON
values and assertions; the reject cases now feed ISO-8601 strings and expect a
parse error. Verified locally: Swift, Dart and Elixir ClientSpec all pass.
…ements

Structural invariants (one source file = one test file; identical docs/shape;
idempotency), a 10th test-parity dimension, per-finding why_not_surfaced +
why_not_caught fields, multi-round loop-until-dry, and a hard WONTFIX deny-list.
…/AK/AU-resid/N1)

Code fixes: csharp/node/swift/elixir AL decompression-error wrapping; node+dart N1
redirect body-replay guard scoped to 307/308; php AU-resid discriminator throw.
Identical canonical tests for all 5 findings added across all 12 SDKs per the
fix-time parity rule. Goldens not yet regenerated.
swift: AL decompression guard now gates on gzip magic bytes (Linux URLSession
auto-decompresses but leaves the Content-Encoding header, so the residual-header
heuristic wrongly threw on every response); platform-guarded zlib decode.
rust/elixir: N1 302-proceeds test asserted the guard predicate deterministically
instead of a chasm round-trip (chasm can't model https->http; returned 405).
…ats (D1/U1/U2/D2)

D1: standalone Bearer + ApiKey authenticator tests in the 11 non-java SDKs.
U1: ServerConfiguration + ServerVariable tests across all 12.
U2: ApiResult tests across all 12.
D2: README Caveats section added to java/kotlin/csharp/python/rust.
Each new test registered in its Better<Lang>Codegen; all 12 ClientSpecs green.
The Gap AL guard used full-line // comments in DefaultApiClient; the swift and
node FormattingSpec (generatedCodeShouldNotContainInlineComments) require block
/* */ comments in generated production code. Convert them; behaviour unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant