[elixir] Add library option with Req-based client (tesla remains default)

Merged
NE
2mo ago
+5812-94

Closes #24834.

Adds a library option to the Elixir client generator, following the pattern used by the Ruby, Python and Java generators, so the client can be generated on top of Req instead of Tesla.

  • library=tesla (default): the Tesla templates are untouched and the generated client behaves exactly as before. The only change to samples/client/petstore/elixir comes from the shared typespec() fix below, which corrects specs for false-mapped responses in both libraries; #24841 makes the same correction on its own.
  • library=req: new template set under elixir/libraries/req/ (api.mustache, connection.ex.mustache, request_builder.ex.mustache). Models, deserializer, mix/config/README templates are shared.

What the Req client does

  • Connection.new/1 accepts the same options as the Tesla client (base_url, user_agent, token/token_scopes with a fetcher, username/password, bearer_token) and returns a Req.Request. A req_options keyword (per client, or via config :<package>, <Module>.Connection, req_options: [...]) replaces Tesla's middleware/adapter configuration.
  • Same request semantics as the Tesla client: parameter locations, Tesla-compatible query/form flattening, multipart through Req's form_multipart (JSON parts carry application/json, files stream), binary bodies passed through untouched, JSON bodies encoded with the built-in JSON module so models keep deriving JSON.Encoder.
  • Same response semantics: RequestBuilder.evaluate_response/2 decodes mapped statuses through the shared Deserializer, returns the whole %Req.Response{} for schema-less mappings (as Tesla returns %Tesla.Env{}), {:ok, %Req.Response{}} for a 2xx the spec does not declare, {:error, %Req.Response{}} for other unmapped statuses and {:error, exception} for transport errors.
  • Cookie parameters (in: cookie) are rendered into a single cookie header (OpenAPI form/explode=false serialization for arrays and objects); a nil body sends no body. Req's automatic retry and redirect following are disabled by default to match Tesla; they can be re-enabled through req_options.
  • Typespecs use Req.Request.t() / Req.Response.t() when library=req, and responses that map to false contribute Req.Response.t() to the {:ok, ...} union rather than the declared schema type, matching what the request builder actually returns.
  • Dependency: {:req, "~> 0.6.0 or ~> 0.7.3"}. 0.6.0 is the first release with the multipart header-escaping fix; 0.7.0–0.7.2 are excluded because they rejected string multipart part names and turned GET requests with a body into POST.

Also in this PR

  • bin/configs/elixir-req.yaml and the generated samples/client/petstore/elixir-req sample, added to .github/workflows/samples-elixir.yaml (same OTP/Elixir matrix, same petstore service).
  • Codegen unit tests for the option (default, req, unknown library, generation through CodegenConfigurator).
  • Regenerated docs/generators/elixir.md.

Related

The same cookie / optional-body-comma / undeclared-2xx fixes for the default Tesla templates are in #24841, which is independent of this PR. Cookie parameters are implemented for the req library here; the generator's ParameterFeature.Cookie stays excluded until #24841 adds them to the default Tesla templates, so this PR does not overstate the generator's capabilities.

Not addressed in either PR, present in both clients today and worth separate issues: array query parameters always use bracket style (#23339); path parameters are not percent-encoded; request Content-Type/Accept are not derived from consumes/produces.

Verification

  • mvn test -Dtest='Elixir*': 6 tests pass.
  • samples/client/petstore/elixir-req: mix compile --warnings-as-errors, mix test (30 tests, against swaggerapi/petstore), mix dialyzer all pass.
  • samples/client/petstore/elixir: regenerated (typespec corrections only); mix test 12 pass.
  • bin/utils/detect_carriage_return.sh and tab check clean.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples: ./mvnw clean package, ./bin/generate-samples.sh bin/configs/elixir*.yaml, ./bin/utils/export_docs_generators.sh.
  • File the PR against the correct branch: master (upcoming minor release).
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

cc @wing328 @mrmstn