Skip to content

Openapi client rest deep dive - #37

Merged
noha merged 7 commits into
masterfrom
openapi-client-rest-deep-dive
Aug 1, 2026
Merged

Openapi client rest deep dive#37
noha merged 7 commits into
masterfrom
openapi-client-rest-deep-dive

Conversation

@noha

@noha noha commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

No description provided.

noha and others added 7 commits August 1, 2026 14:59
New OAExampleDocuments class bundles the six example documents from
OAI/OpenAPI-Specification (_archive_/schemas/v3.0/pass/, Apache-2.0,
YAML converted to JSON) - the same fixtures the OAI project itself
uses to verify its meta-schema accepts real, non-trivial specs.
Complements the hand-written OADocumentValidatorTests branch-coverage
cases with realistic documents nobody hand-crafted for this codebase.

All six validate successfully against OADocumentValidator. Round-
tripping them through OpenAPI's object model (fromString:/specString)
surfaced three additional real bugs, fixed in the following commits.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
visitOpenApi: unconditionally sent components/schemas while normalizing
schemas before serialization - any real document without a components
object at all (both api-with-examples and callback-example, two of the
six official OAI examples, have none) crashed specString entirely with
'receiver of "schemas" is nil'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The example accessor existed but had no setter and its NeoJSON mapping
was commented out, so any document using the (spec-legal, and used by
the official uspto.gov example) inline example field on a Media Type
Object - as opposed to a named entry under examples - failed to read
with MessageNotUnderstood on #example:.

It's mapped without a valueSchema (not as an OAExample): per spec this
field is arbitrary example data, not an Example Object with its own
summary/description/value/externalValue wrapper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OALink existed as a declared class but had never been implemented -
zero instance variables, no neoJsonMapping:, so any document using
components.links (a legitimate, spec-standard feature for HATEOAS-
style responses - used by the official link-example.json) failed with
NeoJSONMappingNotFound: No mapping found for OALink.

Added the six Link Object fields (operationId, operationRef,
parameters, requestBody, description, server) as plain accessors -
parameters/requestBody/server are raw passthrough data per spec
(runtime expressions / arbitrary values), matching how OAMediaTypeObject
handles its own raw fields.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OAHeaderParametersLocation and OACookieParameterLocation implemented
extractParameter: (for the REST/server side, reading incoming
requests) but had no write:value:to: at all. OAParameter>>copyFrom:to:
dispatches to this method for every 'in: #header' or 'in: #cookie'
parameter when a client builds an outgoing request
(OAOperation>>applyParameters:builder:), so any operation with a
header or cookie parameter (e.g. a custom API-key header) crashed
with #doesNotUnderstand: #write:value:to:. This was previously
unreachable by any test - path and query parameters were covered,
header/cookie were not.

Adds OARequestBuilder>>addHeaderParameter:value: (sets the header
directly on the ZnClient request) and addCookieParameter:value:
(accumulates multiple cookie parameters into a single 'Cookie'
header, name=value pairs joined by '; '), and wires the two location
classes to call them.
OAMediaTypeObject>>writeBody:builder: and writeFormBody:builder: sent
#isAnyObject unconditionally to the body schema, but #isAnyObject is
only implemented on JSONSchemaObject. Any body schema that isn't
type:object crashed with #doesNotUnderstand: #isAnyObject when
building a real request - this includes bare string/array/number
schemas, and (much more commonly) any allOf-composed schema, since
JSONSchemaDefinition>>allOf: never sets schemaClass, so a schema
defined purely via allOf (no direct type/properties keyword, e.g.
the OAI petstore-expanded example's Pet = allOf[NewPet, {id}])
resolves to JSONSchemaAnyObject via #asJSONSchema.

Both methods now check (schema isKindOf: JSONSchemaObject) before
asking isAnyObject, and degrade to a plain passthrough for any other
schema kind - the same behavior already used for a JSONSchemaObject
with no declared properties.

Note: this only fixes the client-side write (request-body encoding)
path. Reading/deserializing an allOf-composed value already works
correctly for validation (JSONSchemaAllOfConstraint is attached to
JSONSchemaAnyObject regardless of schemaClass), but
JSONSchemaAnyObject>>read: is still a raw passthrough that neither
merges allOf branches' properties nor instantiates a typed object -
left undocumented as a known gap, see final report.
Pet class>>resetInstances existed but was never called by any test.
Pet instances accumulates in a class-side IdentitySet
(OpenAPIRestPetTests/OpenAPIHeaderTests each add pets without
clearing it), so repeated test runs within the same image session
grow the collection without bound (verified: two back-to-back runs
of the 4-test package left 16 Pet instances alive). Tests still
passed regardless since they only assert on ids they just created,
but this is a real test-isolation gap. Added setUp to
OpenAPIRestTests (the shared base class for all REST test cases)
calling Pet resetInstances.
@noha
noha merged commit f937262 into master Aug 1, 2026
3 of 4 checks passed
@noha
noha deleted the openapi-client-rest-deep-dive branch August 1, 2026 15:29
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