OpenAPI, brought up to spec

The last post told JSONSchema's origin story: it exists because ApptiveGrid needed OpenAPI, and OpenAPI needs a way to describe request and response bodies. OpenAPI is the other half of that story - a Pharo implementation of the OpenAPI 3 specification, three packages deep: `OpenAPI-Core` models a document as an object tree and can validate a whole document against the spec, `OpenAPI-Client` drives an HTTP client from a loaded document, and `OpenAPI-REST` is the server-side counterpart, routing requests on top of Zinc.

What that buys you day to day: parse a document, ask it questions, check whether it's actually valid, and eventually make or serve real HTTP calls from it.

api := OpenAPI fromString: someOpenApiJsonString.
api info title.        "e.g. 'Swagger Petstore'"
api paths keys.         "e.g. #('/pets' '/pets/{petId}')"

OADocumentValidator isValidDocument: someOpenApiJsonString.   "-> true/false"

`OpenAPI-Client` goes a step further and turns that parsed document into something you can actually call: subclass `OpenApiClient`, point it at a document and a server, and fire requests by operation id rather than hand-building URLs.

client := MyApiClient new.
client baseUri: 'https://api.example.com/v1'.
client call: 'listPets' withArguments: (Dictionary new at: 'limit' put: 5; yourself).

Then, same as JSONSchema, ApptiveGrid moved on. Not because OpenAPI failed at anything, but because ApptiveGrid's own type system - `AGType`, with its lookups, references, formulas and attachments - turned out to express more than OpenAPI's schemas ever could. OpenAPI stopped being the central way ApptiveGrid describes itself; its code wasn't even loaded in the dev image anymore.

It still needed to catch up, though. `BaselineOfOpenAPI` had JSONSchema pinned to `0.3`, a tag from 2020, from back when JSONSchema was still FileTree instead of Tonel - years behind, and increasingly incompatible with the JSONSchema work described in the last post. A fresh OpenAPI release first froze the old state, then the pin came out entirely: OpenAPI now tracks JSONSchema's `master` branch, with one explicit goal behind it - both should actually conform to their respective specs, not just parse the happy path.

Conforming to a spec means checking a whole document against it, not just individual values, which OpenAPI had never really done. That's `OADocumentValidator` now: it builds the official OAI meta-schema and validates a document against it, with real error messages instead of a bare yes/no. To test it against something more honest than hand-written minimal documents, it now bundles six real OpenAPI 3.0 documents straight from the official specification repository - petstore, petstore-expanded, api-with-examples, callback-example, link-example, uspto - the same fixtures the spec's own authors use to test their meta-schema.

Real documents found real bugs that invented test cases never would have - a crash on a whole class of valid documents, a field that could be read but not written back out, a spec-standard piece of the format that turned out to be an untouched empty stub. Real responses even exposed a regex dialect Pharo's engine didn't understand, buried in the official meta-schema itself. All of it got fixed, on both sides of the JSONSchema/OpenAPI boundary.

Six official OpenAPI documents flowing through OADocumentValidator, with the real issues they surfaced labeled along the way

The most convincing proof, though, came from trying to actually drive something real: pointing `OpenAPI-Client` at Stripe's own public OpenAPI 3.0 spec. The suspected blocker turned out to be a red herring; the real one was blunter - the client only ever wrote request bodies as JSON, while almost all of Stripe's endpoints are form-urlencoded. Once that was fixed, firing real requests at a local mock of Stripe's API surfaced a handful of further bugs, including one quietly wrong for every array ever read, not just Stripe's.

One thing didn't get chased: OpenAPI 3.1 and 3.2 switched their schema dialect from the older, draft-04-like style 3.0 uses - a `nullable` keyword, boolean `exclusiveMinimum`, one `type` per schema - to full JSON Schema 2020-12 plus OpenAPI's own vocabulary on top: `$dynamicRef`/`$dynamicAnchor`, `$ref` allowed alongside sibling keywords, `unevaluated*` applicators. None of that machinery exists in JSONSchema yet, and building it is its own large project. So, deliberately, only 3.0 is validated. A 3.1 meta-schema already builds in the image, but without the 2020-12 machinery behind it, it degrades to accepting anything - a known, honest limit rather than a silently wrong answer.

OpenAPI isn't coming back as ApptiveGrid's central mechanism - `AGType` earned that role. But there's a plausible way back in: generating an OpenAPI document per project space, so the outside world has something concrete to integrate against. That's exactly why the three layers stayed this loosely coupled instead of merging into one another - if that day comes, OpenAPI shouldn't need to drag its whole dependency chain back in just to describe one space.

The code is on github if you want to read ahead - the README covers installation, the client's full parameter style/explode matrix, and what's deliberately still not supported, in more depth than this post does.

 
Found something worth flagging? Send feedback.
This work is licensed under CC BY 4.0.