Add Sinatra integration (OpenapiFirst::Sinatra)#483
Conversation
640794f to
de8bce8
Compare
jzobel
left a comment
There was a problem hiding this comment.
Nicely done — reads clean and is unusually well-tested and documented for an experimental extension. Inline notes below; two more worth flagging beyond those:
1 (design) — routing is delegated to Sinatra/Mustermann, but validation uses the OAD template. The two matchers can diverge at the edges (trailing slash, dot-in-segment, encoding, greediness): a request Mustermann matches is validated against the template it was defined with, using the params Mustermann extracted — not openapi_first's own path matching. Fine for the covered cases; worth a docs note that the two routers' path semantics should be kept aligned. (Mustermann itself is healthy — 4.0.0, Apr 2026 — so this is about semantic divergence, not dependency risk.)
5 (minor) — build_operation_index is first-wins on duplicate operationIds (index[operation_id] ||= …): a duplicate is silently dropped. Invalid OAD, but a warn/raise would beat silent.
| # The parsed request body | ||
| # @return [Sinatra::IndifferentHash] | ||
| def parsed_body | ||
| ::Sinatra::IndifferentHash[openapi_request.parsed_body] |
There was a problem hiding this comment.
parsed_body assumes an object body: Sinatra::IndifferentHash[openapi_request.parsed_body] raises for a top-level JSON array/scalar body (and for a bodyless request, where parsed_body is nil). Consider guarding for non-Hash bodies, or documenting it as object-only and pointing to openapi_request.parsed_body for arrays/scalars.
|
|
||
| def validate(request, route_params:) | ||
| parsed_request, error = parse_request(request, route_params:) | ||
| def validate(request, path_params:) |
There was a problem hiding this comment.
Naming flag: route_params: → path_params: is clearer (they are path params). If Request#validate / parse_request count as public / manual-use API, this is a breaking keyword rename — worth a CHANGELOG line; ignore if they're internal.
| ``` | ||
|
|
||
| The HTTP method and path for each route come from the operationId. | ||
| Request validation is called automatically for these operations |
There was a problem hiding this comment.
Formatting nits: add a blank line before ## 3.4.3, give this line a trailing period, and drop the trailing whitespace on the closing ``` fence a few lines up. (README also has trailing whitespace on the blank line inside the create_pet example, ~L442.)
Resolve the path for an operation by operationId, filling in path parameters. Extracted from the Sinatra integration, where it backs the operation_url helper. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Sinatra extension to define routes by referencing OpenAPI operations,
so URLs and HTTP methods live only in the description:
require 'openapi_first/sinatra'
class PetsApi < Sinatra::Base
register OpenapiFirst::Sinatra
openapi 'openapi.yaml'
operation :index_pets do |params|
json index_pets(params[:filter])
end
end
The HTTP method and path for each route come from the operationId.
Request validation is called automatically for these operations.
A Sinatra extension to define routes by referencing OpenAPI operations, so URLs and HTTP methods live only in the API description:
The HTTP method and path for each route come from the operationId. Request validation is called automatically for these operations.
Why?
Related to #365