Skip to content

Added OpenAPI client successful response mode with typed error exceptions - #761

Open
GoodforGod wants to merge 2 commits into
masterfrom
feature/openapi-generator-response-mode-successful
Open

Added OpenAPI client successful response mode with typed error exceptions#761
GoodforGod wants to merge 2 commits into
masterfrom
feature/openapi-generator-response-mode-successful

Conversation

@GoodforGod

@GoodforGod GoodforGod commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Added OpenAPI client successful response mode with typed error exceptions

EN

Added clientResponseMode=SUCCESSFUL for generated OpenAPI clients so successful calls can return narrowed success response types while error responses are exposed through typed per-operation exceptions. The default remains SEALED, preserving existing generated client contracts unless the new mode is explicitly enabled.


RU

Добавлен режим clientResponseMode=SUCCESSFUL для OpenAPI клиентов: успешные ответы могут возвращаться как суженные success-типы, а error/default ответы превращаются в типизированные per-operation exception. Поведение по умолчанию остается SEALED, поэтому существующие контракты без явного включения нового режима не меняются.


  • Added clientResponseMode=SEALED | SUCCESSFUL, with SEALED as the default compatibility mode.
  • Added narrowed successful client return types in SUCCESSFUL mode when the OpenAPI response set allows it.
  • Added operation-level *SuccessfulResponseMapper generation for operations with error/default responses, including cases where successful responses are ambiguous and the return type stays sealed.
  • Added typed per-operation client response exceptions that carry the parsed error response and preserve buffered raw response body bytes.

Design

clientResponseMode controls the shape of generated client contracts.

In default SEALED mode, the generator keeps the old behavior: every operation returns the full sealed response hierarchy and each status code is mapped through @ResponseCodeMapper.

@ResponseCodeMapper(code = 200, mapper = PetsApiClientResponseMappers.CreatePet200ApiResponseMapper.class)
@ResponseCodeMapper(code = 400, mapper = PetsApiClientResponseMappers.CreatePet400ApiResponseMapper.class)
PetsApiResponses.CreatePetApiResponse createPet(@Json Pet pet);

In SUCCESSFUL mode, operations with error/default responses use one operation-level mapper. If the successful response shape is unambiguous, the method return type is narrowed to the successful subtype.

@Mapping(PetsApiClientResponseMappers.CreatePetSuccessfulResponseMapper.class)
PetsApiResponses.CreatePetApiResponse.CreatePet200ApiResponse createPet(@Json Pet pet);

If success responses are ambiguous, for example 200 with body and 204 without body, the method keeps the sealed return type but still uses one operation-level mapper. This keeps success typing correct while giving the same error-exception behavior.

@Mapping(PetsApiClientResponseMappers.AmbiguousPetSuccessfulResponseMapper.class)
PetsApiResponses.AmbiguousPetApiResponse ambiguousPet();

The generated *SuccessfulResponseMapper owns response dispatch for the operation:

  • 2xx responses are returned normally.
  • declared error/default responses are parsed into their generated response subtype and wrapped into a typed per-operation exception.
  • unknown responses still use the standard HttpClientResponseException fallback.

Error bodies are buffered before parsing. The mapper replays the buffered body through SimpleHttpClientResponse, so typed error parsing can consume the body while the thrown exception still exposes the original bytes. If parsing fails, the generated mapper throws HttpClientResponseException with the buffered body and adds the parsing failure as suppressed.

try {
  _response = this.createPet400ResponseMapper.apply(_bufferedResponse.response());
} catch (Exception e) {
  throw responseException(response, _bufferedResponse.body(), e);
}
throw new PetsApi.PetsApiCreatePetHttpClientResponseException(
    response.code(),
    response.headers(),
    _response,
    _bufferedResponse.body()
);

The typed exception keeps both layers of information: structured generated response for declared errors and raw body bytes through the base HttpClientResponseException.

class PetsApiModelErrorHttpClientResponseException extends HttpClientResponseException {
    private final ModelError content;

    public PetsApiModelErrorHttpClientResponseException(int code, HttpHeaders headers,
        ModelError content, byte[] body) {
      super(code, headers, body);
      this.content = content;
    }

    public ModelError getContent() {
      return this.content;
    }
  }

@GoodforGod GoodforGod added this to the v2.0.0 milestone Aug 3, 2026
@GoodforGod
GoodforGod requested a review from Squiry August 3, 2026 08:33
@GoodforGod GoodforGod added new feature New feature request module: openapi Related to Openapi module labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Test Results

761 tests   752 ✅  30m 50s ⏱️
100 suites    9 💤
100 files      0 ❌

Results for commit 524ccf4.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Dependency Update Report

Update level: patch

Found 10 dependency updates.

gradle/libs.versions.toml

  • grpc-java: 1.82.2 -> 1.82.3
  • apache-httpclient (org.apache.httpcomponents.client5:httpclient5, inline:143): 5.6.2 -> 5.6.3
  • s3client-aws (software.amazon.awssdk:s3, inline:248): 2.47.5 -> 2.47.6
  • commons-codec: 1.22.0 -> 1.22.1
  • zeebe: 8.9.12 -> 8.9.14
  • kotlin-stdlib: 2.4.0 -> 2.4.10
  • swagger-coreline: 2.2.52 -> 2.2.53
  • swagger-parser: 2.1.45 -> 2.1.46
  • jspecify (org.jspecify:jspecify, inline:76): 1.0.0 -> 1.0.1
  • classgraph (io.github.classgraph:classgraph, inline:168): 4.8.184 -> 4.8.186

…ions

Added `clientResponseMode=SUCCESSFUL` to narrow generated client return types to successful responses where possible while preserving sealed responses when success variants are ambiguous. Error and default responses are now routed through operation-level mappers that parse typed error responses and throw per-operation `HttpClientResponseException` subclasses with buffered response bodies.

- Added `SEALED` as the default client response mode to preserve existing generated client behavior.
- Added operation-level `*SuccessfulResponseMapper` generation for operations with error/default responses, including ambiguous successful response shapes.
- Added buffered error body replay so typed error parsing uses the original body and parse failures still expose the response body on the thrown exception.
Improved `clientResponseMode=SUCCESSFUL` typed error exceptions to generate one API-level exception per unique error body type instead of one exception per operation. Shared exceptions now expose parsed `content` directly while status code, headers, and raw body remain available from the base `HttpClientResponseException`.
@GoodforGod
GoodforGod force-pushed the feature/openapi-generator-response-mode-successful branch from 56d8b6c to 524ccf4 Compare August 3, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module: openapi Related to Openapi module new feature New feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants