micronaut-projects / micronaut-projects/micronaut-openapi

micronaut-openapi 5.0.2 / openapi-generator 7.0.0: broken oneOf + discriminator codegen for allOf-composed branches

Open
#2,797 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
114
Forks
121
Avg merge
2d 17h
Merged PRs (30d)
25

Description

### Expected Behavior

## Summary

When a `oneOf` schema with a `discriminator.mapping` has branches that are themselves
`allOf`-composed (a common pattern for "base envelope + specific payload"), the generator
fails to resolve the discriminator mapping targets by name, silently falls back to synthetic
`OneOf` / `OneOf1` model names for the branches — but the generated
`@JsonSubTypes` annotation on the parent interface still references the **original, unresolved**
branch names, which are never generated. The result does not compile.

This reproduces with `io.micronaut.openapi` Gradle plugin `5.0.2` (which pulls in
`io.micronaut.openapi:micronaut-openapi-generator:7.0.0`). It does **not** reproduce with
`io.micronaut.openapi` `4.6.1` (`micronaut-openapi-generator:6.12.2`), which generates the
expected `StatusEvent` / `ErrorEvent` class names.

## Reproduction

This is a fully standalone Gradle project. (see the attached tar.gz)

```bash
./gradlew generateEventsOpenApiModels
```

### Input

Two files under `src/main/resources/contracts/`:

- `events-api.json` — a minimal OpenAPI 3.1.1 document with one endpoint whose request body
is `$ref`'d from an external JSON-Schema file.
- `event-defs.json` — a JSON-Schema (draft-07 style) file defining:
- `BaseEvent` — a plain object schema (envelope fields).
- `StatusEvent`, `ErrorEvent` — each `allOf: [{"$ref": "#/definitions/BaseEvent"}]` plus their
own `properties` (this is the "envelope + payload" composition pattern).
- `DomainEvents` — `oneOf: [StatusEvent, ErrorEvent]` with a `discriminator.mapping` keyed by
a `type` constant, pointing at the two schemas above by `$ref`.

### Observed output

```
Failed to lookup the schema 'StatusEvent' when processing the discriminator mapping of oneOf/anyOf. Please check to ensure it's defined properly.
Failed to lookup the schema 'ErrorEvent' when processing the discriminator mapping of oneOf/anyOf. Please check to ensure it's defined properly.
```

Generated files under `build/generated/openapi/generateEventsOpenApiModels/.../dto/`:

- `DomainEventsOneOf.java` (should be `StatusEvent.java`)
- `DomainEventsOneOf1.java` (should be `ErrorEvent.java`)
- `DomainEvents.java` — the discriminator interface, which still contains:

```java
@JsonSubTypes({
@JsonSubTypes.Type(value = ErrorEvent.class, name = "com.example.event.Error"),
@JsonSubTypes.Type(value = StatusEvent.class, name = "com.example.event.Status"),
})
public interface DomainEvents {
```

`ErrorEvent` and `StatusEvent` are never generated (only `DomainEventsOneOf`/`DomainEventsOneOf1`
are), so this interface fails to compile with `cannot find symbol`.

### Expected output

`StatusEvent.java` and `ErrorEvent.java` should be generated directly (as they are with the
4.6.1 / 6.12.2 generator pair), and the `@JsonSubTypes` annotation on `DomainEvents` should
reference those same generated classes.

## Workaround attempted

Setting `modelNameMapping` in the `openapi { server(...) { ... } }` DSL block
(`modelNameMapping.put("DomainEventsOneOf", "StatusEvent")`) had no effect on the generated
output — the synthetic names and the broken `@JsonSubTypes` reference are unchanged.

[bugreport-micronaut-openapi-oneof-discriminator.tar.gz](https://github.com/user-attachments/files/31215173/bugreport-micronaut-openapi-oneof-discriminator.tar.gz)

### Actual Behaviour

_No response_

### Steps To Reproduce

_No response_

### Environment Information

- `io.micronaut.library` / `io.micronaut.openapi` Gradle plugin: `5.0.2`
- `io.micronaut.openapi:micronaut-openapi-generator`: `7.0.0` (transitively resolved)
- Gradle: 9.7.0
- JDK: 21 (also reproduces on 25)

### Example Application

_No response_

### Version

5.0.2

Contributor guide

No contributing guide indexed for this repository

Research direction

Run ./gradlew generateEventsOpenApiModels in the attached standalone project and inspect src/main/resources/contracts/events-api.json and event-defs.json. Compare the generated DomainEventsOneOf.java, DomainEventsOneOf1.java, and DomainEvents.java with the output from the 4.6.1/6.12.2 generator pair. Done means StatusEvent.java and ErrorEvent.java are generated and DomainEvents references those same classes in @JsonSubTypes.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, openapi
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
56/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.