Problem
#149 replaced a hardcoded next unless ... == "query" check in RequestParams#extract_non_body_params with root_location = location_resolver.resolve(...); next if root_location == "body", so any nested Hash root's resolved location that isn't literally "body" now gets stamped directly onto the emitted Parameter — an allowlist became a denylist.
ParamLocationResolver#explicit_location returns whatever string the user typed in documentation: { in: ... } or param_type: ..., downcased, with no further validation, and ApiModel::Parameter doesn't validate location either (@location = location.to_s). Three concrete cases where this now emits an invalid document instead of the previous silent drop:
# 1. Typo'd location — emitted verbatim
optional :filter, type: Hash, documentation: { in: "quer" } do
optional :kind, type: String
end
# => "in": "quer" in the generated OAS document
# 2. grape-swagger-style formData — emitted, but wrongly lowercased
optional :filter, type: Hash, documentation: { param_type: "formData" } do
optional :kind, type: String
end
# => "in": "formdata" — wrong even for OAS 2.0, where lib/grape_oas/exporter/oas2/parameter.rb
# correctly writes camelCase "formData" elsewhere
# 3. path on a route with no matching template segment
optional :filter, type: Hash, documentation: { in: "path" } do
optional :min, type: Integer
end
post("items") { {} }
# => filter[min] emitted with in: "path", required: true (forced by build_parameter),
# but /items has no {filter[min]} path template — invalid per the OAS spec
grape-swagger migrants are called out in AGENTS.md as a worse-informed user segment likely to write param_type: 'formData' by reflex, so case 2 is not just theoretical.
Fix
Restore an allowlist instead of inverting to a denylist in lib/grape_oas/api_model_builders/request_params.rb's nested-param branch:
next unless %w[query header path cookie].include?(root_location)
cookie is valid in OAS 3 and was previously unreachable through this path — worth a regression test alongside the allowlist fix, along with a case for a typo'd/unrecognized location being dropped rather than emitted.
Scope note
This isn't a #149 regression in the sense of "worked before, broken now" — pre-#149 behavior for all three inputs above was a silent drop of the parameter, which was already the bug #149 fixed for the legitimate query/header/path cases. This follow-up narrows the fix so malformed input degrades back to a silent drop instead of an invalid emitted document.
Problem
#149 replaced a hardcoded
next unless ... == "query"check inRequestParams#extract_non_body_paramswithroot_location = location_resolver.resolve(...); next if root_location == "body", so any nested Hash root's resolved location that isn't literally"body"now gets stamped directly onto the emittedParameter— an allowlist became a denylist.ParamLocationResolver#explicit_locationreturns whatever string the user typed indocumentation: { in: ... }orparam_type: ..., downcased, with no further validation, andApiModel::Parameterdoesn't validatelocationeither (@location = location.to_s). Three concrete cases where this now emits an invalid document instead of the previous silent drop:grape-swagger migrants are called out in AGENTS.md as a worse-informed user segment likely to write
param_type: 'formData'by reflex, so case 2 is not just theoretical.Fix
Restore an allowlist instead of inverting to a denylist in
lib/grape_oas/api_model_builders/request_params.rb's nested-param branch:cookieis valid in OAS 3 and was previously unreachable through this path — worth a regression test alongside the allowlist fix, along with a case for a typo'd/unrecognized location being dropped rather than emitted.Scope note
This isn't a #149 regression in the sense of "worked before, broken now" — pre-#149 behavior for all three inputs above was a silent drop of the parameter, which was already the bug #149 fixed for the legitimate
query/header/pathcases. This follow-up narrows the fix so malformed input degrades back to a silent drop instead of an invalid emitted document.