Skip to content

Derive envelope type from OpenAPI metadata during client generation #32

Description

@bsayli

Problem statement

OpenAPI Generics currently requires the application-owned envelope type to be configured independently on both the producer and client/codegen sides.

On the producer side:

openapi-generics:
  envelope:
    type: io.example.contract.ApiResponse

On the client/codegen side:

<additionalProperties>
  <additionalProperty>
    openapi-generics.envelope=io.example.contract.ApiResponse
  </additionalProperty>
</additionalProperties>

The producer already resolves the concrete Java envelope type while building the OpenAPI contract.

However, this identity is not currently included in the generated vendor extension metadata. The client generator must therefore receive the same information again through an external codegen property.

This introduces duplicated configuration and makes the client/codegen setup unnecessarily dependent on information that is already known on the producer side.

The OpenAPI contract should be the authoritative source for the envelope semantics required by downstream generators.


Proposed solution

Introduce a new canonical vendor extension:

x-api-wrapper-type

The extension should contain the fully qualified Java type of the resolved generic envelope.

Example:

ApiResponsePagingCustomerDto:
  x-api-wrapper: true
  x-api-wrapper-type: io.example.contract.ApiResponse
  x-api-wrapper-datatype: PagingCustomerDto
  x-data-container: Paging
  x-data-container-type: io.example.contract.Paging
  x-data-item: CustomerDto

The client generator should derive the envelope type directly from x-api-wrapper-type.

This makes newly generated OpenAPI contracts self-describing and removes the need to repeat the envelope type in client/codegen configuration.


Producer changes

Add a new canonical vendor extension key:

public static final String API_WRAPPER_TYPE = "x-api-wrapper-type";

The producer should add this metadata to every projected wrapper schema for which the concrete envelope type has been resolved.

Example:

ServiceResponseCustomerDto:
  x-api-wrapper: true
  x-api-wrapper-type: io.github.blueprintplatform.openapi.generics.contract.response.ServiceResponse
  x-api-wrapper-datatype: CustomerDto
  x-data-item: CustomerDto

For application-owned envelopes:

ApiResponseWindowCustomerDto:
  x-api-wrapper: true
  x-api-wrapper-type: io.example.contract.ApiResponse
  x-api-wrapper-datatype: WindowCustomerDto
  x-data-container: Window
  x-data-container-type: io.example.contract.Window
  x-data-item: CustomerDto

The resolved envelope type should be written directly to each projected wrapper schema so that every schema remains independently interpretable by the client/codegen pipeline.


Client/codegen changes

Update the reconstruction pipeline so that the wrapper type is derived directly from the x-api-wrapper-type schema metadata.

The existing client-side property:

openapi-generics.envelope

is no longer required once the producer and codegen components are upgraded to 1.2.1.

The property may remain present in existing build configuration without affecting generation, but it should no longer participate in envelope type resolution.

The OpenAPI contract becomes the single source of truth for wrapper identity.

Users upgrading to 1.2.1 should remove the redundant client-side property from their codegen configuration.

Mixed-version usage, such as generating a client with the 1.2.1 codegen integration from an OpenAPI document produced by an earlier server-starter version, is not part of the supported release alignment.


Alternatives considered

Continue requiring client-side envelope configuration

Rejected because the producer already knows the resolved envelope identity.

Requiring the same value on both sides introduces unnecessary duplication and weakens the OpenAPI contract as the source of code generation metadata.

Keep the client-side property as a fallback

Rejected because it would preserve two independent sources of envelope identity and require precedence rules, conflict handling, warnings, and additional compatibility branches.

The 1.2.1 producer and codegen components are expected to be upgraded together as part of the same release line.

Store the envelope type only on the ignored base schema

Rejected as the primary reconstruction mechanism because generated wrapper schemas should remain independently interpretable by the codegen pipeline.

The resolved type should be available directly on each projected wrapper schema.

Infer the fully qualified type from the schema name

Rejected because schema names are not reliable Java type identities and may be renamed, customized, or collision-resolved.


Expected impact

  • OpenAPI contracts become self-describing for generic envelope reconstruction
  • Client-side envelope configuration is no longer required
  • Reduced producer/client configuration duplication
  • The OpenAPI contract becomes the sole source of wrapper identity
  • Improved symmetry with x-data-container-type
  • Support for multiple envelope identities within the same OpenAPI document
  • Existing builds remain operational when producer and codegen components are upgraded together
  • Redundant client-side envelope configuration can be removed
  • No runtime behavior changes
  • No public Java API removals

Compatibility

This change is additive at the OpenAPI metadata level and does not modify runtime envelope behavior.

Applications upgrading the producer and codegen components together to 1.2.1 continue generating clients without behavioral changes.

The existing client-side openapi-generics.envelope configuration becomes redundant and should be removed during upgrade.

Keeping the property in an existing build does not break generation, but the value is no longer used.

The supported configuration model assumes aligned OpenAPI Generics component versions across the producer and client/codegen pipeline.

Using a 1.2.1 client/codegen integration with an OpenAPI document produced by an earlier server-starter version is outside the supported release alignment because the earlier document does not contain x-api-wrapper-type.


Documentation updates

Update the configuration documentation so that the recommended setup becomes:

Producer

openapi-generics:
  envelope:
    type: io.example.contract.ApiResponse

  containers:
    - type: io.example.contract.Paging
      item-property: content

Client/codegen

No envelope configuration is required.

The client generator derives the envelope type directly from:

x-api-wrapper-type

Existing codegen configuration may temporarily still contain:

<additionalProperties>
  <additionalProperty>
    openapi-generics.envelope=io.example.contract.ApiResponse
  </additionalProperty>
</additionalProperties>

but the property is redundant in 1.2.1 and should be removed during upgrade.


Validation scope

The implementation should include:

  • producer metadata generation tests
  • built-in ServiceResponse<T> validation
  • BYOE ApiResponse<T> validation
  • codegen reconstruction without client-side envelope configuration
  • generated client compilation
  • consumer runtime validation
  • aligned producer and codegen version validation
  • documentation updates

Additional context

Container identity is already represented directly in the OpenAPI contract:

x-data-container-type: io.example.contract.Paging

Envelope identity currently follows a different model and must be supplied externally by the client generator.

Adding x-api-wrapper-type aligns wrapper and container reconstruction under the same contract-driven design.

This work does not change envelope behavior.

It completes the existing metadata contract and removes unnecessary client-side duplication.


Status

Implemented on main.

Planned for 1.2.1.

Implemented in 7548896.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions