# frozen_string_literal: true require 'rspec/rails/api/metadata' module RSpec module Rails module Api module DSL # All these methods will be available in example groups # (anything but 'it', 'example', 'for_code') module ExampleGroup ## # First method to be called in a spec file # as it will initialize the # metadata. # # @param name [String] Resource name # @param description [String] Resource description def resource(name, description = '') metadata[:rra] ||= Metadata.new metadata[:rra].add_resource name, description end ## # Describes an entity # # @param type [Symbol] Name of the entity for reference # @param fields [Hash] Fields declarations # # @return [void] def entity(type, fields) RSpec::Rails::Api::Metadata.add_entity type, fields end ## # Describes request or path parameters # # @param type [Symbol] Name of the parameters set for reference # @param fields [Hash] Fields declarations # # @return [void] def parameters(type, fields) metadata[:rra].add_parameter type, fields end ## # Declares parameters used in URLS (_path_) # Use `fields` or `defined` but not both. # # @param [Hash, nil] fields An attributes declaration # @param [Symbol, nil] defined An entity reference # # @return [void] def path_params(fields: nil, defined: nil) if defined && !metadata[:rra].parameters[defined] raise "Parameter #{defined} was not defined with the 'parameters' method" end fields ||= metadata[:rra].parameters[defined] metadata[:rra].add_path_params fields end ## # Declares parameters for a request body # Use `attributes` or `defined` but not both. # # @param [Hash, nil] attributes An attributes declaration # @param [Symbol, nil] defined An entity reference. # # @return [void] def request_params(attributes: nil, defined: nil) if defined && !metadata[:rra].parameters[defined] raise "Parameter #{defined} was not defined with the 'parameters' method" end attributes ||= metadata[:rra].parameters[defined] metadata[:rra].add_request_params attributes end ## # Declares security schemes valid for this path. It won't be enforced during testing but will complete the # documentation. When the reference does not exist, an exception will be thrown _during_ render, not before. # # @param scheme_references [Array] References to a security scheme defined with the renderer's # `add_security_scheme`. def requires_security(*scheme_references) metadata[:rra].add_security_references(*scheme_references) end ## # Defines a GET action # # @param [String] url URL to test # @param [String] summary What the action does # @param [String] description Longer description # # @return [void] def on_get(url, summary = nil, description = nil, &block) on_action(:get, url, summary, description, &block) end ## # Defines a POST action # # @param [String] url URL to test # @param [String] summary What the action does # @param [String] description Longer description # # @return [void] def on_post(url, summary = nil, description = nil, &block) on_action(:post, url, summary, description, &block) end ## # Defines a PUT action # # @param [String] url URL to test # @param [String] summary What the action does # @param [String] description Longer description # # @return [void] def on_put(url, summary = nil, description = nil, &block) on_action(:put, url, summary, description, &block) end ## # Defines a PATCH action # # @param [String] url URL to test # @param [String] summary What the action does # @param [String] description Longer description # # @return [void] def on_patch(url, summary = nil, description = nil, &block) on_action(:patch, url, summary, description, &block) end ## # Defines a DELETE action # # @param [String] url URL to test # @param [String] summary What the action does # @param [String] description Longer description # # @return [void] def on_delete(url, summary = nil, description = nil, &block) on_action(:delete, url, summary, description, &block) end ## # Adds an HTTP code declaration to metadata, with expected result # If no expectation is provided, the response will be expected to be empty # # @param status_code [Number] Status code to test for # @param description [String] Description of the route/status pair # @param expect_many [Symbol] Check the response for a list of given entity # @param expect_one [Symbol] Check the response for a given entity # @param test_only [Boolean] When true, test the response without filling the documentation # # @return [void] # def for_code(status_code, description = nil, expect_many: nil, expect_one: false, test_only: false, &block) description ||= Rack::Utils::HTTP_STATUS_CODES[status_code] metadata[:rra].add_status_code(status_code, description) unless test_only metadata[:rra].add_expectations(expect_one, expect_many) metadata[:rra_current_example] = metadata[:rra].current_example describe "->#{test_only ? ' test' : ''} #{status_code} - #{description}" do execute_for_code_block(block) end end private ## # Currently fill metadata with the action # # @param [Symbol] action HTTP verb # @param [String] url URL to test # @param [String, nil] summary What the action does # @param [String, nil] description Longer description # # @return [void] def on_action(action, url, summary, description, &block) metadata[:rra].add_action(action, url, summary, description) describe("#{action.upcase} #{url}", &block) end ## # Visit the URL and test response # # @param callback_block [block] Block to execute for testing the response # # @return [void] def execute_for_code_block(callback_block) example 'Test response and create documentation', caller: callback_block.send(:caller) do instance_eval(&callback_block) if callback_block end end end end end end end RSpec::Core::ExampleGroup.extend(RSpec::Rails::Api::DSL::ExampleGroup)