- index components/parameters + requestBodies in OpenAPISpec; resolve bare $ref parameter and requestBody entries through the cycle-guarded resolver; unresolvable refs abort spec parse loudly - reject path placeholders with no matching input-schema property at registration (no more silently percent-encoded literal placeholders) - reject duplicate operationIds and path+method routes in one import batch instead of silent last-write-wins registration - also reject a parameter named 'body' shadowed by requestBody (OAI-07 adjacency, same code path) Verified: cargo test (243), --all-features (322), clippy -D warnings (default + all-features), fmt --check, doc --no-deps
5.5 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| review-001-openapi-import-integrity | Resolve parameter/requestBody $refs; detect import collisions (OAI-04, OAI-05) | completed |
|
narrow | medium | component | implementation |
|
Description
Review 001 findings OAI-04 + OAI-05 — import produces ops that misbehave silently at call time:
- OAI-04 (
openapi_spec.rs:236-262): parameter entries of the form{"$ref": "#/components/parameters/Id"}(extremely common in real specs) have noname, are silently skipped, and never reachresolve_refs_recursive; same forrequestBody: {"$ref": …}. Onlycomponents/schemasis indexed (:162-174). Result: the op registers with{id}in the template butidabsent from the schema; at call time the placeholder is substituted with the literal{owner}text and percent-encoded (%7Bowner%7D) — a well-formed request to a nonsense path, with credentials attached. No error at import or call time. Fix: indexcomponents/parameters+requestBodies, resolve the ref forms, and fail loudly on unresolved path placeholders (import and/or call time). - OAI-05 (
from_openapi.rs:51-65,160): generated operation IDs collide by construction (/x/{id}/yand/x/yboth →get_x_y); the registry silently replaces, so one op shadows another and/searchunder-reports. Detect duplicates within the import batch and fail loudly (or deterministically disambiguate + warn — pick one and document).
Acceptance Criteria
{"$ref": "#/components/parameters/…"}params andrequestBodyrefs resolve into the op's schema (test)- Unresolved path placeholders fail loudly (import-time, or call-time with a loud error — tested, not silently
%7Bowner%7D) - Duplicate operation IDs in one import batch are detected and rejected (or deterministically disambiguated + warned — tested)
cargo testandcargo clippy --all-targets -- -D warningspass
References
- docs/reviews/001-initial-implementation-review.md (Part E, OAI-04, OAI-05)
Notes
Agent fills during implementation. Follows the cycle-guard task so the resolver is already hardened when the new ref kinds are wired through it.
Implementation notes. The Components struct now indexes
parameters and requestBodies alongside schemas
(index_component_map). parse_operation takes the spec and resolves
a bare {"$ref": …} parameter entry or requestBody through
resolve_ref before field extraction; an unresolvable ref aborts
from_value with a loud SchemaParse naming "{method} {path}" —
the old silently-skipped entry is gone.
OAI-04 guard at both layers:
- Import-time (
from_value): a parameter that yields noname/in(including via a failed ref) fails the parse. Ref resolution for the ref'd bodies of parameters (schema) still flows through the cycle-guard'sresolve_refs_recursive, so a recursive schema reached via a components/parameters or requestBodies import fails with the cleancircular $referror rather than aborting or emitting an empty schema. - Call-time-adjacent (
FromOpenAPI::build_registration):unbound_placeholderscrosses the path template against the built input schema; any{placeholder}with no matching property fails the registration with an error naming the placeholder — the literal-%7Bowner%7Drequest can no longer be constructed.
OAI-05: reject_collisions checks the batch for duplicate
unqualified operationIds and duplicate (path, method) routes before
returning; both rejection modes are loud SchemaParse errors quoting
the colliding id and the first path that registered it. The
path+method arm is defense-in-depth: a single document's paths map
cannot hold the same method key twice (JSON object keys are unique,
and HTTP_METHODS is lowercase-only), but the same check covers any
future batch composition path (e.g. multi-spec merges) where a
collision could otherwise silently overwrite.
Summary
Remediated OAI-04 + OAI-05. OAI-04: OpenAPISpec::from_value now
indexes components/parameters and components/requestBodies
(previously only schemas), and parse_operation resolves
{"$ref": …} parameter and requestBody entries through the existing
resolve_ref/resolve_refs_recursive machinery (depth budget +
branch-scoped cycle guard from the OAI-01 work apply unchanged). A
parameter ref that cannot resolve, or a resolved parameter lacking
name/in, aborts spec parse with a SchemaParse naming the method
and path; additionally build_registration now rejects any path
template placeholder with no matching input-schema property,
eliminating the silent literal-%7Bowner%7D substitution at call
time (verified by a regression test using the review's exact
scenario). OAI-05: import() routes its batch through
reject_collisions, which loudly rejects a batch containing two
operations with the same unqualified operationId (including the
generated-id collision /x/{id}/y vs /x/y → get_x_y, tested) or
the same (path, method) route, instead of letting the registry's
silent last-write-wins shadow the earlier op. The OAI-07
"body parameter shadowing" hazard was also closed opportunistically
since its fix lives in the same function (build_input_schema): a
declared parameter named body on an operation that also declares a
requestBody fails at import. Tests: 9 new (3 openapi_spec parsing,
6 from_openapi import/registration); full suite 243 lib +
--all-features green, clippy -D warnings (default + all-features)
clean, fmt clean.