Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

228 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenAPI

Build Status

This is an implementation of the OpenAPI 3.0 specification for the Pharo language. It models an OpenAPI document as an object tree, validates whole documents against the official OAI meta-schema, and can drive an HTTP client from a loaded document. It builds on JSONSchema for schema parsing and validation.

The framework is split into three packages:

  • OpenAPI-Core — the document object model (OpenAPI, OAInfo, OAPathItem, OAOperation, OAParameter, OASchemaDefinition, …), parsing/serialization, and OADocumentValidator for validating a document against the spec.
  • OpenAPI-Client — builds and fires HTTP requests from a loaded document (OpenApiClient, OARequestBuilder), including the full OAI parameter style/explode matrix.
  • OpenAPI-REST — a server-side routing layer on top of Zinc (OpenAPICall, OpenAPIUriSpace). This package is effectively unmaintained legacy code (see What is (not yet) supported) — new work should not depend on it.

Contents

Installation

Load it into Pharo via Metacello:

Metacello new
  repository: 'github://zweidenker/OpenAPI';
  baseline: #OpenAPI;
  load.

This loads the default group (OpenAPI-Core + OpenAPI-REST + OpenAPI-Client, plus their tests). Load a specific group instead if you only need part of it, e.g. load: #('Client') to get OpenAPI-Core + OpenAPI-Client while skipping the REST layer entirely.

Quick start

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

"...and check whether it's actually a valid OpenAPI 3.0 document."
OADocumentValidator isValidDocument: someOpenApiJsonString.   "-> true/false"

Parsing and serializing documents

OpenAPI fromString: parses a JSON document into the full object model (OAInfo, OAPathItem, OAOperation, OAParameter, OASchemaDefinition, …) and resolves $refs within it. The result can be serialized back to JSON:

api := OpenAPI fromString: jsonString.
api specString.   "-> the document, serialized back to JSON"

Parsing/serialization is best-effort, not a strict round-trip guarantee: a small number of schema keyword combinations don't survive specString unchanged (see What is (not yet) supported).

Validating documents

OADocumentValidator checks a whole document against the official OAI 3.0 meta-schema (bundled verbatim, Apache-2.0 — see OAMetaSchema), not just individual parameter values:

OADocumentValidator isValidDocument: jsonStringOrDictionary.        "-> Boolean"
OADocumentValidator validateDocument: jsonStringOrDictionary.       "-> the parsed document, or raises a JSONSchemaError"
OADocumentValidator validationErrorMessageFor: jsonStringOrDictionary.
  "-> nil if valid, otherwise e.g. 'JSONSchemaMissingRequiredProperty: ...'"

The built meta-schema is cached per major.minor version, so repeated validation doesn't rebuild it every time. Only the first validation error is reported — JSONSchema-Core signals on the first constraint violation rather than accumulating a full list.

OAExampleDocuments bundles six real-world OpenAPI 3.0 documents (petstore, petstore-expanded, api-with-examples, callback-example, link-example, uspto) taken verbatim from the official OAI/OpenAPI-Specification repository (Apache-2.0) — the same fixtures the OAI project itself uses to test its meta-schema. Useful as realistic test data beyond hand-written minimal documents.

Building requests with OpenAPI-Client

Subclass OpenApiClient and implement buildOpenApi to supply the parsed document; baseUri sets the server to talk to. Requests are then made by operation ID:

MyApiClient class >> buildOpenApi [
  ^ OpenAPI fromString: myOpenApiJsonString
]

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

call:withArguments: looks up the operation by operationId, builds the request (path/query/header/cookie parameters, JSON or form-urlencoded body as declared by the operation), fires it, and returns the parsed response body on success. A spec-level violation caught before the request is even sent (e.g. a missing required parameter) raises an OAError subclass; any non-2xx response raises OAUnspecifiedError with the parsed error body attached. Nested request bodies (e.g. metadata[key], items[0][price]) are flattened automatically for form-urlencoded bodies.

Parameter locations and styles

All four parameter locations and the full OAI style/explode matrix are supported when building requests:

Location Supported styles Default style Default explode
path simple, label, matrix simple false
query form, spaceDelimited, pipeDelimited, deepObject form true
header simple simple false
cookie form form true

deepObject and form+explode:true on an object both expand into multiple top-level parameters (one per property) rather than a single value, per spec. Cookies are a documented special case: the spec leaves form+explode:true on a non-primitive value undefined for cookies (you can't repeat a cookie name the way you repeat a query key), so arrays/objects are comma-joined into the single cookie value instead.

What is (not yet) supported

This targets OpenAPI 3.0 specifically — not a full implementation of every version or every edge case. Known gaps:

  • 3.1 / 3.2 are not supported, and this is a deliberate decision, not an oversight: their dialect is JSON Schema 2020-12 + the OAS vocabulary, which needs $dynamicRef/$dynamicAnchor resolution, $ref-sibling composition and unevaluated* applicators — machinery JSONSchema-Core doesn't have yet. A 3.1 meta-schema builds but degrades to accepting anything, since it can't yet enforce the 2020-12 keywords.
  • allOf composition of object schemas (the common "extends" pattern, e.g. Pet = allOf[NewPet, {id}]) merges properties/required/additionalProperties for reading. A mixed/primitive allOf (not every branch object-shaped) has no sensible single merge target and stays a raw passthrough.
  • A schema derived purely from allOf (no explicit type keyword of its own) can't re-serialize its type keyword through specString — a real gap, not silently wrong: it raises rather than guessing.
  • OpenAPI-REST (server-side routing) is effectively unmaintained legacy code — parallel to, and superseded by, other routing mechanisms in the wider ApptiveGrid ecosystem.
  • Only local $refs are resolved (inherited from JSONSchema-Core — no remote/external reference fetching).

If you need something that is missing you are welcome to open a pull request, or a ticket in this repository.

Releases

Packages

Used by

Contributors

Languages