Skip to content

feat: AsyncAPI 3.0 and 3.1 support - #1316

Merged
bartveenstra merged 5 commits into
mainfrom
feat/asyncapi-3
Nov 5, 2026
Merged

bartveenstra merged 5 commits into
mainfrom
feat/asyncapi-3

Conversation

@bartveenstra

@bartveenstra bartveenstra commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds AsyncAPI 3.0 and 3.1 support to BOAT, so a service can keep its event contracts next to its REST specs and generate Spring Cloud Stream code from them. See the new AsyncAPI 3.0 and 3.1 section of the plugin README and the 0.19.0 release notes.

An AsyncAPI document is loaded, validated and projected onto an OpenAPI document with one operation per AsyncAPI operation and message. BOAT then bundles and de-duplicates it as usual, so OpenAPI and AsyncAPI documents that share schema files produce the same model classes.

Goals

  • boat:generate-spring-cloud-stream-embedded (generator boat-spring-cloud-stream): generates {Tag}Handler interfaces, {Tag}FunctionsConfiguration with Consumer/Function beans and {Tag}Publisher classes on StreamBridge (grouped by operation tag, or by channel when untagged), plus an asyncapi-bindings.properties with the binding destinations. Whether code is generated for the producing or the consuming side follows each operation's action; inverseOperations generates the other side.
  • boat:generate accepts AsyncAPI input with any generator. Other generators only generate the payload and header models.
  • boat:validate checks AsyncAPI documents against the official AsyncAPI JSON Schema of their version (3.0.0 or 3.1.0), plus reference and schema-format checks.
  • boat:bundle bundles an AsyncAPI document into a single file with de-duplicated components/schemas. Its new projectionOpenApiVersion parameter matches the one on boat:generate.
  • boat:lint, boat:doc and boat:diff skip AsyncAPI documents found in directories; boat:ship ships them as is.

AsyncAPI 3.1

  • Documents declaring asyncapi: 3.1.x load like 3.0.x. 3.1 adds ROS 2 bindings and the AsyncAPI version=3.1.0 schema formats; neither changes what is generated. boat:validate picks the official schema by document version, so a ROS 2 binding passes in a 3.1 document and fails in a 3.0 one.
  • Other versions (2.x, 3.2+) are rejected with a message naming the supported ones.

Schemas

  • Payload and header schemas can be in AsyncAPI or OpenAPI 3.0 format, or JSON Schema draft-04, -06, -07, 2019-09 or 2020-12. They can be inline, or Multi Format Schema Objects under components/schemas, where the component key names the model.
  • projectionOpenApiVersion (default 3.0.3) sets the OpenAPI version of the projection. Use 3.1.0 for 2019-09/2020-12 schemas and keywords such as const and if/then. Warnings name the schemas a given version cannot express; referenced schema files are checked too.

Fixes in existing code

  • Bundling OpenAPI 3.1: swagger-parser follows allOf/oneOf/items refs only for ComposedSchema/ArraySchema, but OpenAPI 3.1 schemas are parsed as JsonSchema. External refs under those and the other JSON Schema keywords stayed relative to their own file, and so did nested discriminator mappings.
    • OpenApi31SchemaRefs rebases them when BoatCache loads a schema, and resolves the leftovers to components after the parser has finished.
    • It only runs for 3.1, so 3.0 bundling is unchanged. This also benefits plain OpenAPI 3.1 specs.
  • DeduplicateSchemasTransformer: now also rewrites discriminator mappings that pointed at a merged duplicate.

Review notes

  • The official AsyncAPI 3.0.0 and 3.1.0 JSON Schemas are not committed: boat-engine unpacks schemas/3.0.0-without-$id.json and schemas/3.1.0-without-$id.json from the org.webjars.npm:asyncapi__specs WebJar (asyncapi-specs.version, 6.11.1) into target/classes during generate-resources. The WebJar is not a runtime dependency.
  • New dependency: com.networknt:json-schema-validator 1.5.9 in boat-engine, used only for that schema check.
  • MavenProjectCompiler (scaffold test helper) is now public and reads the settings file from the MavenProjectCompiler.settings system property, so the scaffold and codegen tests run outside GitHub Actions.
  • The integration test examples under boat-maven-plugin/src/it/example/boat-generate/asyncapi/ cover inline schemas, and schemas reused between OpenAPI and AsyncAPI.

Test plan

  • mvn -B -Dgpg.skip -pl boat-engine,boat-scaffold,boat-maven-plugin -am verify including invoker ITs (local)
  • New tests: AsyncApiLoaderTest#loadsAsyncApi31, AsyncApiValidatorTest#validatesAgainstTheSchemaOfTheDocumentVersion, BundlerTests#bundlesTheReferencesOfOpenApi31Schemas (fails without the 3.1 fix), DeduplicateSchemasTransformerTests#rewritesDiscriminatorMappingsOfMergedSchemas, AsyncApiMojoTests#bundlesAsyncApiOntoTheProjectionOpenApiVersion, and the AsyncAPI loader/validator/projector/bundler/codegen suites
  • Used from a downstream service (Factory approval service): validate, bundle and generate for two CloudEvents with JSON Schema 2020-12 payloads, plus a Spring Cloud Stream test-binder round trip
  • CI

bartveenstra and others added 3 commits November 3, 2026 14:56
- New boat:generate-spring-cloud-stream-embedded goal (boat-spring-cloud-stream generator):
  Spring Cloud Stream handlers, functions and StreamBridge publishers per AsyncAPI operation.
- boat:generate accepts AsyncAPI documents with any generator (models only for others).
- boat:validate validates AsyncAPI 3.0 documents; boat:bundle bundles them, with a
  projectionOpenApiVersion parameter matching boat:generate.
- JSON Schema draft-04 to 2020-12 payloads, inline or as Multi Format Schema Objects
  under components/schemas.
- Fix: bundling OpenAPI 3.1 resolves external refs under oneOf, items, not, if/then
  and the other JSON Schema keywords, and in nested discriminator mappings.
- Fix: deduplicateSchemas rewrites discriminator mappings of merged schemas.

Co-authored-by: Cursor <cursoragent@cursor.com>
…i/specs WebJar

boat-engine unpacks schemas/3.0.0-without-$id.json from org.webjars.npm:asyncapi__specs at build time instead of committing a copy.

Co-authored-by: Cursor <cursoragent@cursor.com>
Documents declaring asyncapi 3.1.x are loaded like 3.0.x and validated against the official 3.1.0 JSON Schema; both schemas are unpacked from the @asyncapi/specs WebJar. The AsyncAPI 3.1.0 schema formats are accepted for payloads and headers.

Co-authored-by: Cursor <cursoragent@cursor.com>
@bartveenstra bartveenstra changed the title feat: AsyncAPI 3.0 support feat: AsyncAPI 3.0 and 3.1 support Nov 3, 2026
…annel extensions

Adds a CloudEvents fixture with two channels, untagged send and receive
operations, schemas shared across events and x-producers/x-consumers on
each channel. Covers validation, bundling, projection and Spring Cloud
Stream generation.

Co-authored-by: Cursor <cursoragent@cursor.com>
@bartveenstra
bartveenstra requested a review from jjjasper November 4, 2026 08:29
checkResponseExampleFulfill force-unwrapped properties and items.properties,
so object/array schemas without a properties block crashed the lint with a
NullPointerException. Treat missing properties as nothing to check, and
add regression tests for object and array-of-object schemas.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

@bartveenstra
bartveenstra merged commit 69eff13 into main Nov 5, 2026
4 checks passed
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