Skip to main content

RSwag (request specs)

Overview

OpenAPI is generated from request specs in each gem. Root spec/ holds only the dummy app and helpers.

When to use

  • Every new or changed route (including async default and /sync variants).
  • You need security and permission metadata in the published spec.

Example

1. Add a request spec file

decidim-restfull-widgets/spec/requests/decidim/api/rest_full/widgets/widgets_controller_show_spec.rb

2. Require the swagger helper

require "swagger_helper"

3. Describe the controller and path

RSpec.describe Decidim::Api::RestFull::Widgets::WidgetsController do
path "/widgets/{id}" do
get "Show widget" do
tags "Widgets"
produces "application/json"
security [{ credentialFlowBearer: ["widgets"] }]
operationId "widgetShow"

4. Wrap examples in describe_api_endpoint

      describe_api_endpoint(
controller: Decidim::Api::RestFull::Widgets::WidgetsController,
action: :show,
security_types: [:credentialFlow],
scopes: ["widgets"],
permissions: ["widgets.read"]
) do

5. Assert with run_test!

        let(:id) { widget.id }
let(:component_id) { component.id }
let(:space_id) { participatory_process.id }
let(:space_manifest) { "participatory_processes" }

response "200", "Widget found" do
schema "$ref" => Decidim::RestFull::Core::DefinitionRegistry.reference(:widget_item_response)
run_test! { |ex| expect(JSON.parse(ex.body)["data"]["id"]).to eq(widget.id.to_s) }
end
end

Async mutation (decidim-restfull-forms/spec/requests/decidim/api/rest_full/forms/questions_controller_spec.rb):

response "202", "Job accepted" do
run_test! do |response|
expect(response).to have_http_status(:accepted)
expect(JSON.parse(response.body)).to include("job_id")
end
end

6. Register spec globs on the engine

decidim-restfull-widgets/lib/decidim/rest_full/widgets/engine.rb

ext.rswag_specs File.join(Widgets::ENGINE_ROOT, "spec/requests/decidim/api/rest_full/widgets/**/*_spec.rb")

7. Regenerate OpenAPI

yarn gen:openapi-spec
# or: bin/openapi-stale --yes (Docker)

Rules

RuleDetail
No spec → no pathUndocumented routes are not in openapi.json.
Async + syncDocument 202 on default mutation; 200/201 on *_sync where applicable.
Shared exampleslocalized params, paginated params, describe_api_endpoint from core test helpers.
Paths registryspec/rest_full_swagger_spec_paths.rb + GemSpecPaths
CasePath
Exampledecidim-restfull-proposals/spec/requests/.../proposals_controller_show_spec.rb
Forms asyncdecidim-restfull-forms/spec/requests/.../questions_controller_spec.rb

See also