Comparing AsyncAPI specifications

The same breaking change detector, now fluent in event-driven APIs.

Event-driven APIs break too. They just do it quietly, in a consumer nobody remembers subscribing to that topic.

This feature is available from v0.4.0

openapi-changes speaks AsyncAPI 3 natively. Same commands, same reports, same time machine.

openapi-changes summary previous-asyncapi.yaml asyncapi.yaml

The format is detected from the document, so there is nothing to configure and no flag to remember.

AsyncAPI 3.x is supported. AsyncAPI 2.x is not. Both sides of a comparison must use the same format, so comparing OpenAPI against AsyncAPI is rejected.

Every command works

All five comparison commands accept AsyncAPI documents.

openapi-changes console previous-asyncapi.yaml asyncapi.yaml openapi-changes html-report previous-asyncapi.yaml asyncapi.yaml && open report.html openapi-changes markdown-report previous-asyncapi.yaml asyncapi.yaml openapi-changes report previous-asyncapi.yaml asyncapi.yaml | jq

See Command Arguments for the shared flags.


Git history works too

The time machine does not care which format we feed it.

openapi-changes summary HEAD~1:asyncapi.yaml ./asyncapi.yaml openapi-changes summary ./ asyncapi.yaml

Multi-file specs with $ref references are resolved from the same revision, so the working tree never leaks into a historical comparison.


What it looks like

Here is summary running against the AsyncAPI samples in the openapi-changes repo.

Breaking Highlights
  - Changed AsyncAPI Order id type: string -> integer
  - Changed AsyncAPI channels orders address: orders.v1 -> orders.v2
  - Changed AsyncAPI operations receiveOrder action: receive -> send
  - Changed AsyncAPI production host: broker.example.test:9092 -> events.example.test:9092

├─┬info
│ └──[M] version: 1.0.0 -> 2.0.0
├─┬servers
│ └─┬production
│   └──[M] host: broker.example.test:9092 -> events.example.test:9092 {X}
├─┬components
│ └─┬schemas
│   └─┬Order
│     └─┬properties
│       └─┬id
│         └──[M] type: string -> integer {X}
├─┬channels
│ └─┬orders
│   └──[M] address: orders.v1 -> orders.v2 {X}
└─┬operations
  └─┬receiveOrder
    └──[M] action: receive -> send {X}

The command exits with an error when it finds breaking changes, which is exactly what we want in CI.

The reports use AsyncAPI’s own language instead of squeezing everything into an HTTP shape. Servers, channels, operations, messages and components each get their own named sections. Channels use hash icons, and operations carry send and receive badges in the document tree, the explorer and the detail views.


Configuring breaking changes

The default rules already know about channels, operations, messages, servers and the rest. To change them, split the rules file into named sections.

openapiRules:
  info:
    description:
      modified: false
asyncapiRules:
  channel:
    address:
      modified: false
  operation:
    action:
      modified: false
schemaRules:
  schema:
    type:
      modified: false
  • openapiRules configures OpenAPI comparisons.
  • asyncapiRules configures AsyncAPI objects, such as channel, operation, message and server.
  • schemaRules configures the JSON Schemas embedded in AsyncAPI documents. It has no effect on OpenAPI.

Every section is optional, and any other top level key is rejected.

A rules file without these sections stays OpenAPI-only, and using one against AsyncAPI documents is an error. If changes-rules.yaml is sitting in the working directory, move its rules under openapiRules first.

Schemas written in other languages, such as Avro, are compared as opaque content. Changing their format or their content is breaking by default.

See Configuring breaking changes for how the rules work.


Reading the JSON

JSON reports describe what was compared in a specification object.

{
  "specification": {
    "kind": "asyncapi",
    "originalVersion": "3.0.0",
    "modifiedVersion": "3.0.0"
  }
}

summary, markdown-report and html-report count a change once, even when a shared definition reports it several times. report keeps every occurrence, so its numbers can be higher. That difference is deliberate.


What’s next?