From d278b0118c0929ce9ecc07df1bc9f65341cfcd4c Mon Sep 17 00:00:00 2001 From: Norbert Hartl Date: Sat, 1 Aug 2026 17:07:56 +0200 Subject: [PATCH 1/3] Document why the acceptOpenApi: visitor stubs are harmless, not fix them Traced (2026-08-01) whether JSONSchemaObject/JSONSchemaArray>>acceptOpenApi: is actually reached, rather than relying on the prior "seems unreachable" assumption from the Stripe session: it IS reached, via OAVisitor>>visitComponents: (self visitAll: aComponents schemas) visiting each top-level named schema directly. But by that point in OAReferenceResolveVisitor>>visitOpenApi:, those schemas are already fully resolved via #processSchema:/JSONSchemaReferenceResolveVisitor earlier in the very same method, before super visitOpenApi: is called. Recursing here would just repeat already-done work - returning self unchanged is correct given today's call order. Replaced the stale #todo flag with this traced reasoning instead of leaving it looking like an outstanding task. Co-Authored-By: Claude Sonnet 5 --- source/OpenAPI-Core/JSONSchemaArray.extension.st | 7 ++++--- source/OpenAPI-Core/JSONSchemaObject.extension.st | 12 +++++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/source/OpenAPI-Core/JSONSchemaArray.extension.st b/source/OpenAPI-Core/JSONSchemaArray.extension.st index 7f7f523..706954a 100644 --- a/source/OpenAPI-Core/JSONSchemaArray.extension.st +++ b/source/OpenAPI-Core/JSONSchemaArray.extension.st @@ -1,8 +1,9 @@ Extension { #name : 'JSONSchemaArray' } { #category : '*OpenAPI-Core' } -JSONSchemaArray >> acceptOpenApi: aVisitor [ - self flag: #todo. - "should be recurse into sub elements later" +JSONSchemaArray >> acceptOpenApi: aVisitor [ + "See JSONSchemaObject>>acceptOpenApi: - same confirmed-harmless reasoning applies + (reached via OAVisitor>>visitComponents:, but only after schemas are already fully + resolved via #processSchema: earlier in the same pass)." ^ self ] diff --git a/source/OpenAPI-Core/JSONSchemaObject.extension.st b/source/OpenAPI-Core/JSONSchemaObject.extension.st index 45e2d09..33b1806 100644 --- a/source/OpenAPI-Core/JSONSchemaObject.extension.st +++ b/source/OpenAPI-Core/JSONSchemaObject.extension.st @@ -1,8 +1,14 @@ Extension { #name : 'JSONSchemaObject' } { #category : '*OpenAPI-Core' } -JSONSchemaObject >> acceptOpenApi: aVisitor [ - self flag: #todo. - "should be recurse into sub elements later" +JSONSchemaObject >> acceptOpenApi: aVisitor [ + "Confirmed reached (2026-08-01, traced): OAVisitor>>visitComponents: visits + components.schemas' values directly (self visitAll: aComponents schemas), hitting + this for every top-level named schema. But by the time that runs, those schemas are + already fully resolved - OAReferenceResolveVisitor>>visitOpenApi: converts and + resolves them via #processSchema:/JSONSchemaReferenceResolveVisitor earlier in the + very same pass, before calling super visitOpenApi: (which is what reaches here). + Recursing into sub-elements here would repeat work already done; returning self + unchanged is correct given today's call order, not a missing feature." ^ self ] From 2f2e4dce49a91656a198867c6aa7ac2c1bfdc065 Mon Sep 17 00:00:00 2001 From: Norbert Hartl Date: Sat, 1 Aug 2026 17:07:56 +0200 Subject: [PATCH 2/3] Fix the latent /spec crash: isAbstract and PetsAPI's rootCallClass OpenAPICall class>>isAbstract only treated the literal OpenAPICall as abstract (self = OpenAPICall), not user-defined intermediate base classes like OpenAPIBasePetCall which also have no #path of their own. Combined with PetsAPI class>>rootCallClass answering the literal OpenAPICall instead of its own OpenAPIBasePetCall root, OpenAPI>>buildPaths (via #withAllSubclasses, which includes the receiver) tried to build a pathItem for OpenAPIBasePetCall itself and crashed with doesNotUnderstand: #path. This made /spec (OpenAPISpecCall>>get calls PetsAPI new specString) unreachable - verified live, and now covered by a real end-to-end test hitting /spec through the REST delegate. isAbstract is now structural (not (self respondsTo: #path)) instead of a literal-class comparison, so it correctly excludes any shared base class regardless of name. rootCallClass now answers OpenAPIBasePetCall, scoping buildPaths to PetsAPI's own call hierarchy instead of every OpenAPICall subclass in the whole image - and lives as an OpenAPI-REST-Tests extension (not in PetsAPI's own OpenAPI-Core-Tests home package), since OpenAPIBasePetCall is only defined there. Co-Authored-By: Claude Sonnet 5 --- source/OpenAPI-Core-Tests/PetsAPI.class.st | 5 ----- .../OpenAPIRestPetTests.class.st | 18 ++++++++++++++++++ source/OpenAPI-REST-Tests/PetsAPI.extension.st | 11 +++++++++++ source/OpenAPI-REST/OpenAPICall.class.st | 7 ++++++- 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 source/OpenAPI-REST-Tests/PetsAPI.extension.st diff --git a/source/OpenAPI-Core-Tests/PetsAPI.class.st b/source/OpenAPI-Core-Tests/PetsAPI.class.st index 07d4c26..1a464d2 100644 --- a/source/OpenAPI-Core-Tests/PetsAPI.class.st +++ b/source/OpenAPI-Core-Tests/PetsAPI.class.st @@ -5,11 +5,6 @@ Class { #package : 'OpenAPI-Core-Tests' } -{ #category : 'as yet unclassified' } -PetsAPI class >> rootCallClass [ - ^ OpenAPICall -] - { #category : 'accessing' } PetsAPI >> openapi [ ^ '3.0.2' diff --git a/source/OpenAPI-REST-Tests/OpenAPIRestPetTests.class.st b/source/OpenAPI-REST-Tests/OpenAPIRestPetTests.class.st index eaebb94..12f2f74 100644 --- a/source/OpenAPI-REST-Tests/OpenAPIRestPetTests.class.st +++ b/source/OpenAPI-REST-Tests/OpenAPIRestPetTests.class.st @@ -10,6 +10,24 @@ OpenAPIRestPetTests >> rootCallClass [ ^ OpenAPIBasePetCall ] +{ #category : 'tests' } +OpenAPIRestPetTests >> testGetSpecCallReturnsGeneratedDocument [ + "Regression: OpenAPICall class>>isAbstract only treated the literal OpenAPICall as + abstract (not OpenAPIBasePetCall, a shared intermediate base with no #path of its + own), and PetsAPI class>>rootCallClass answered the literal OpenAPICall instead of + its own OpenAPIBasePetCall root - together, OpenAPI>>buildPaths (via + #withAllSubclasses) tried to build a pathItem for OpenAPIBasePetCall itself, + crashing with doesNotUnderstand: #path. /spec (OpenAPISpecCall>>get calls + PetsAPI new specString) was therefore unreachable." + | response | + response := self delegate handleRequest: (ZnClient new + url: '/spec'; + method: #GET; + prepareRequest) request. + self assert: response isSuccess. + self assert: (response entity contents includesSubstring: '/pets') +] + { #category : 'tests' } OpenAPIRestPetTests >> testGetPetCall [ diff --git a/source/OpenAPI-REST-Tests/PetsAPI.extension.st b/source/OpenAPI-REST-Tests/PetsAPI.extension.st new file mode 100644 index 0000000..36f7147 --- /dev/null +++ b/source/OpenAPI-REST-Tests/PetsAPI.extension.st @@ -0,0 +1,11 @@ +Extension { #name : 'PetsAPI' } + +{ #category : '*OpenAPI-REST-Tests' } +PetsAPI class >> rootCallClass [ + "OpenAPIBasePetCall, not the literal OpenAPICall - the latter is the base of every + OpenAPICall subclass in the whole image, not just PetsAPI's own call hierarchy, + which made buildPaths (via #withAllSubclasses) pull in unrelated call classes too. + Lives as an OpenAPI-REST-Tests extension (not in PetsAPI's own OpenAPI-Core-Tests + home package) since OpenAPIBasePetCall is only defined there." + ^ OpenAPIBasePetCall +] diff --git a/source/OpenAPI-REST/OpenAPICall.class.st b/source/OpenAPI-REST/OpenAPICall.class.st index 11c0348..33afeba 100644 --- a/source/OpenAPI-REST/OpenAPICall.class.st +++ b/source/OpenAPI-REST/OpenAPICall.class.st @@ -80,7 +80,12 @@ OpenAPICall class >> implementedMethods [ { #category : 'testing' } OpenAPICall class >> isAbstract [ - ^ self = OpenAPICall + "Structural check instead of a literal-class comparison: a class is abstract for + URI-space/path-building purposes if it does not (itself or via inheritance) answer a + concrete #path - the one thing every real endpoint must define. Catches + user-defined intermediate base classes (e.g. OpenAPIBasePetCall), not just the + literal OpenAPICall root, which the previous 'self = OpenAPICall' check missed." + ^ (self respondsTo: #path) not ] { #category : 'testing' } From 2dee82a9254b4a024095218b2ea3ab478b7e07cd Mon Sep 17 00:00:00 2001 From: Norbert Hartl Date: Sat, 1 Aug 2026 17:07:56 +0200 Subject: [PATCH 3/3] Update README build badge from Travis CI to GitHub Actions CI moved to GitHub Actions a while ago; the badge still pointed at the long-dead Travis CI build. Co-Authored-By: Claude Sonnet 5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79163d2..30274ce 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # OpenAPI -[![Build Status](https://travis-ci.org/zweidenker/OpenAPI.svg?branch=master)](https://travis-ci.org/zweidenker/OpenAPI) +[![Build Status](https://github.com/zweidenker/OpenAPI/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/zweidenker/OpenAPI/actions/workflows/build.yml)