Skip to content

re #214 fix(scaffold): name RPC action paths by their noun resource (#214 Phase 5) - #222

Merged
tonydspaniard merged 1 commit into
masterfrom
214-phase5-security-naming
Jun 5, 2026
Merged

re #214 fix(scaffold): name RPC action paths by their noun resource (#214 Phase 5)#222
tonydspaniard merged 1 commit into
masterfrom
214-phase5-security-naming

Conversation

@tonydspaniard

Copy link
Copy Markdown
Member

Part of #214Phase 5 (security & naming). Final phase; closes the epic.

Problem

RPC-style path leaves were treated as resources. GET /pet/findByStatus derived App\FindByStatu\FindPetsByStatussingularize("findByStatus") stripped the trailing s to FindByStatu, and the action segment became the class namespace instead of the real resource (pet).

Change (PathDeriver)

An action leaf is now detected by whether its first camelCase word is a known verb (find, upload, login, …) — which cleanly separates findByStatus (verb find) from a camelCase noun like userProfiles (noun user, still singularized to UserProfile) and findings (one word, not find). resourceSegment walks back past action leaves to the nearest noun; singularize leaves action leaves intact.

Path Before After
GET /pet/findByStatus App\FindByStatu\FindPetsByStatus App\Pet\FindPetsByStatus
POST /pet/{petId}/uploadImage App\UploadImage\UploadFile App\Pet\UploadFile
POST /user/login App\Login\LoginUser App\User\LoginUser
GET /store/inventory App\Inventory\GetInventory unchanged (inventory is the resource)

Two find* operations on one resource now collide on api/pet/find.yaml and disambiguate by operationId (find-pets-by-status.yaml / find-pets-by-tags.yaml) — no specs lost.

Also closing out Phase 5 scope

  • Path-param declared types already import correctly (a path param with schema: {type: integer} becomes int) — Phase 2 resolved this; the old "always string" note was stale (verified on the Petstore's petId).
  • Security schemes / security are surfaced by design — Altair has no auth/middleware spec construct to generate into, so the requirement is reported (so you wire auth yourself) rather than silently dropped. Documented as a deliberate non-goal in coverage.md, like oneOf.

Verification

  • New tests: action paths derive the noun resource; camelCase noun resources still singularize; filename() + resolveFilenames disambiguation for find*; bare single-segment action verbs fall back to themselves.
  • Real Petstore: 19 specs / 18 warnings / 0 unmapped, openapi:roundtrip --check clean; the three target paths now derive App\Pet\… / App\User\….
  • Full QA (cache-free): CS · PHPStan L8 no-baseline · Rector full-tree clean; 328 scaffold tests green.
  • code-reviewer: APPROVE (0 CRITICAL/0 HIGH); the one MEDIUM (camelCase-noun misclassification) was fixed (not just documented) by the first-camel-word-verb heuristic, plus the suggested filename/collision/bare-verb tests added.

Closes the #214 epic — coverage map updated to "complete".

…214 Phase 5)

Phase 5 (final) of the OpenAPI bidirectional-fidelity epic.

RPC-style path leaves were treated as resources: `GET /pet/findByStatus` derived
`App\FindByStatu\FindPetsByStatus` — `singularize("findByStatus")` stripped the
trailing 's' to "FindByStatu", and the action became the class namespace.

PathDeriver now recognises an action leaf by whether its first camelCase word is
a known verb (`find`, `upload`, `login`, ...): `findByStatus` → `find` (action),
`uploadImage` → `upload` (action), while a noun leaf like `userProfiles` →
`user` (resource, still singularized to `UserProfile`) and `findings` → one word
(resource). `resourceSegment` walks back past action leaves to the nearest noun,
and `singularize` leaves action leaves intact. So:

- GET  /pet/findByStatus        -> App\Pet\FindPetsByStatus
- POST /pet/{petId}/uploadImage -> App\Pet\UploadFile
- POST /user/login             -> App\User\LoginUser
- GET  /store/inventory        -> App\Inventory\GetInventory (unchanged: inventory is the resource)

Two find* operations on one resource now collide on `api/pet/find.yaml` and
disambiguate by operationId (`find-pets-by-status.yaml` / `find-pets-by-tags.yaml`),
so no specs are lost — the real Petstore stays at 19 specs / 18 warnings / 0
unmapped, roundtrip clean.

Also closes out the epic's Phase 5 scope:
- Path-param declared types already import (a path param with `schema: {type:
  integer}` becomes `int`) — Phase 2 resolved that; the old "always string" note
  was stale.
- Security schemes / `security` requirements are surfaced by design (Altair has
  no auth/middleware spec construct to generate into), documented as a deliberate
  non-goal in coverage.md rather than a pending gap.
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