Skip to content

Feat/add support for non exhaustive union types - #2083

Draft
acote-coveo wants to merge 2 commits into
opensearch-project:mainfrom
acote-coveo:feat/add-support-for-non-exhaustive-union-types
Draft

Feat/add support for non exhaustive union types#2083
acote-coveo wants to merge 2 commits into
opensearch-project:mainfrom
acote-coveo:feat/add-support-for-non-exhaustive-union-types

Conversation

@acote-coveo

@acote-coveo acote-coveo commented Aug 3, 2026

Copy link
Copy Markdown

Add _Custom variant kind to non-exhaustive tagged unions

Description

OpenSearch plugins can introduce aggregation types, query types, processors, analyzers, and mapping properties that aren't modeled in the Java client's generated code. Today, encountering one of these at deserialization time throws a JsonException, making the client unusable against clusters with such plugins installed.

This PR introduces a Kind._Custom enum member on tagged unions marked x-non-exhaustive: true in the OpenAPI spec. When the deserializer encounters an unknown discriminator value it captures the raw JSON as JsonData under the _Custom kind instead of failing. The original type name is preserved via _customKind() and the data round-trips through serialization.

Changes

Bug-fix (independent, first commit):

  • JsonData._DESERIALIZER — the lambda-based deserializer silently ignored the pre-consumed event argument in the three-arg deserialize overload. Replaced with an explicit JsonpDeserializerBase that delegates to parser.getValue() when the event is already consumed.

Feature:

Area What changed
OpenAPI spec Added x-non-exhaustive: true to 10 union schemas (Aggregate, Query, SpanQuery, Property, Processor, Analyzer, CharFilterDefinition, TokenFilterDefinition, TokenizerDefinition, FieldSuggester)
Codegen model OpenApiSchema parses the extension; SpecTransformer propagates it; TaggedUnionShape gains a nonExhaustive flag and a _customKind field
Codegen templates TaggedUnionShape.mustache emits Kind._Custom, _customKind(), _isCustom(), _custom(), Builder._custom(type, data), and an inner CustomVariant wrapper class. Deserialize/Serialize templates handle the fallback path
Runtime TaggedUnion interface adds a default _customKind() method; ExternallyTaggedUnion uses it for typed-key serialization
Generated code 9 union classes regenerated with the new support

Usage

// Reading an unknown aggregation type returned by a plugin
Aggregate agg = response.aggregations().get("foo");
if (agg._isCustom()) {
    String typeName = agg._customKind();   // e.g. "my_plugin_agg"
    JsonData raw = agg._custom();           // full JSON body
}

// Building a custom variant
Aggregate custom = new Aggregate.Builder()
    ._custom("my_plugin_agg", JsonData.of(Map.of("score", 42)))
    .build();

Related changes

opensearch-project/opensearch-api-specification#1182

Checklist

  • New functionality is covered by tests
  • CHANGELOG.md updated
  • User guide updated (guides/json.md)
  • CI passes

The lambda-based deserializer ignored the three-argument overload,
causing it to re-read from the parser after the event was already
consumed. Replace with an explicit JsonpDeserializerBase that delegates
to parser.getValue() when the event is provided.
Tagged unions annotated with x-non-exhaustive in the OpenAPI spec now
emit a Kind._Custom enum member so that unknown discriminator values
(e.g. plugin-provided aggregation types) deserialize into a lossless
JsonData wrapper instead of throwing.

Affected unions: Aggregate, Query, SpanQuery, Property, Processor,
Analyzer, CharFilterDefinition, TokenFilterDefinition,
TokenizerDefinition.
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