module OpenApi::Rswag::Specs::ExampleGroupHelpers

Public Instance Methods

description(value = nil) click to toggle source

NOTE: 'description' requires special treatment because ExampleGroup already defines a method with that name. Provide an override that supports the existing functionality while also setting the appropriate metadata if applicable

Calls superclass method
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 29
def description(value = nil)
  return super() if value.nil?

  metadata[:operation][:description] = value
end
examples(example = nil) click to toggle source

NOTE: Similar to 'description', 'examples' need to handle the case when being invoked with no params to avoid overriding 'examples' method of rspec-core ExampleGroup

Calls superclass method
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 177
def examples(example = nil)
  return super() if example.nil?

  metadata[:response][:examples] = example
end
header(name, attributes) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 163
def header(name, attributes)
  metadata[:response][:headers] ||= {}

  if attributes[:type] && attributes[:schema].nil?
    attributes[:schema] = {type: attributes[:type]}
    attributes.delete(:type)
  end

  metadata[:response][:headers][name] = attributes
end
merge_other_examples!(example_metadata) click to toggle source

checks the examples in the parameters should be able to add $ref and externalValue examples. This syntax would look something like this in the integration _spec.rb file

request_body_json schema: { '$ref' => '#/components/schemas/blog' },

examples: [:blog, {name: :external_blog,
                    externalValue: 'http://api.sample.org/myjson_example'},
                  {name: :another_example,
                    '$ref' => '#/components/examples/flexible_blog_example'}]

The first value :blog, points to a let param of the same name, and is used to make the request in the integration test (it is used to build the request payload)

The second item in the array shows how to add an externalValue for the examples in the requestBody section The third item shows how to add a $ref item that points to the components/examples section of the swagger spec.

NOTE: that the externalValue will produce valid example syntax in the swagger output, but swagger-ui will not show it yet

# File lib/open_api/rswag/specs/example_group_helpers.rb, line 199
def merge_other_examples!(example_metadata)
  # example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
  content_node = example_metadata[:operation][:requestBody][:content]['application/json']
  return unless content_node

  external_example = example_metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:name].is_a?(Hash) && p[:name][:externalValue] } || {}
  ref_example = example_metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:name].is_a?(Hash) && p[:name]['$ref'] } || {}
  examples_node = content_node[:examples] ||= {}

  nodes_to_add = []
  nodes_to_add << external_example unless external_example.empty?
  nodes_to_add << ref_example unless ref_example.empty?

  nodes_to_add.each do |node|
    json_request_examples = examples_node ||= {}
    other_name = node[:name][:name]
    other_key = node[:name][:externalValue] ? :externalValue : '$ref'
    if other_name
      json_request_examples.merge!(other_name => {other_key => node[:param_value]})
    end
  end
end
parameter(attributes) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 135
def parameter(attributes)
  if attributes[:in] && attributes[:in].to_sym == :path
    attributes[:required] = true
  end

  if attributes[:type] && attributes[:schema].nil?
    attributes[:schema] = {type: attributes[:type]}
  end

  if metadata.key?(:operation)
    metadata[:operation][:parameters] ||= []
    metadata[:operation][:parameters] << attributes
  else
    metadata[:path_item][:parameters] ||= []
    metadata[:path_item][:parameters] << attributes
  end
end
path(template, metadata = {}, &block) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 8
def path(template, metadata = {}, &block)
  metadata[:path_item] = { template: template }
  describe(template, metadata, &block)
end
request_body(attributes) click to toggle source

MUST HAVES need to run “`npm install“` in rswag-ui dir to get assets to load not sure if its an asset issue or what but this should load => localhost:3000/api-docs/index.html TODO: fix examples in the main README

# File lib/open_api/rswag/specs/example_group_helpers.rb, line 51
def request_body(attributes)
  # can make this generic, and accept any incoming hash (like parameter method)
  attributes.compact!

  if metadata[:operation][:requestBody].blank?
    metadata[:operation][:requestBody] = attributes
  elsif metadata[:operation][:requestBody] && metadata[:operation][:requestBody][:content]
    # merge in
    content_hash = metadata[:operation][:requestBody][:content]
    incoming_content_hash = attributes[:content]
    content_hash.merge!(incoming_content_hash) if incoming_content_hash
  end
end
request_body_json(schema:, required: true, description: nil, examples: nil) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 65
def request_body_json(schema:, required: true, description: nil, examples: nil)
  passed_examples = Array(examples)
  content_hash = { 'application/json' => { schema: schema, examples: examples }.compact! || {} }
  request_body(description: description, required: required, content: content_hash)
  if passed_examples.any?
    # the request_factory is going to have to resolve the different ways that the example can be given
    # it can contain a 'value' key which is a direct hash (easiest)
    # it can contain a 'external_value' key which makes an external call to load the json
    # it can contain a '$ref' key. Which points to #/components/examples/blog
    passed_examples.each do |passed_example|
      if passed_example.is_a?(Symbol)
        example_key_name = passed_example
        # TODO: write more tests around this adding to the parameter
        # if symbol try and use save_request_example
        param_attributes = { name: example_key_name, in: :body, required: required, param_value: example_key_name, schema: schema }
        parameter(param_attributes)
      elsif passed_example.is_a?(Hash) && passed_example[:externalValue]
        param_attributes = { name: passed_example, in: :body, required: required, param_value: passed_example[:externalValue], schema: schema }
        parameter(param_attributes)
      elsif passed_example.is_a?(Hash) && passed_example['$ref']
        param_attributes = { name: passed_example, in: :body, required: required, param_value: passed_example['$ref'], schema: schema }
        parameter(param_attributes)
      end
    end
  end
end
request_body_multipart(schema:, description: nil) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 104
def request_body_multipart(schema:, description: nil)
  content_hash = { 'multipart/form-data' => { schema: schema }}
  request_body(description: description, content: content_hash)

  schema.extend(Hashie::Extensions::DeepLocate)
  file_properties = schema.deep_locate ->(_k, v, _obj) { v == :binary }
  hash_locator = []

  file_properties.each do |match|
    hash_match = schema.deep_locate ->(_k, v, _obj) { v == match }
    hash_locator.concat(hash_match) unless hash_match.empty?
  end

  property_hashes = hash_locator.flat_map do |locator|
    locator.select { |_k,v| file_properties.include?(v) }
  end

  existing_keys = []
  property_hashes.each do |property_hash|
    property_hash.keys.each do |k|
      if existing_keys.include?(k)
        next
      else
        file_name = k
        existing_keys << k
        parameter name: file_name, in: :formData, type: :file, required: true
      end
    end
  end
end
request_body_text_plain(required: false, description: nil, examples: nil) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 92
def request_body_text_plain(required: false, description: nil, examples: nil)
  content_hash = { 'test/plain' => { schema: {type: :string}, examples: examples }.compact! || {} }
  request_body(description: description, required: required, content: content_hash)
end
request_body_xml(schema:,required: false, description: nil, examples: nil) click to toggle source

TODO: add examples to this like we can for json, might be large lift as many assumptions are made on content-type

# File lib/open_api/rswag/specs/example_group_helpers.rb, line 98
def request_body_xml(schema:,required: false, description: nil, examples: nil)
  passed_examples = Array(examples)
  content_hash = { 'application/xml' => { schema: schema, examples: examples }.compact! || {} }
  request_body(description: description, required: required, content: content_hash)
end
response(code, description, metadata = {}, &block) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 153
def response(code, description, metadata = {}, &block)
  metadata[:response] = { code: code, description: description }
  context(description, metadata, &block)
end
run_test!(&block) click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 222
def run_test!(&block)
  # NOTE: rspec 2.x support
  if RSPEC_VERSION < 3
    before do
      submit_request(example.metadata)
    end

    it "returns a #{metadata[:response][:code]} response" do
      assert_response_matches_metadata(metadata)
      block.call(response) if block_given?
    end
  else
    before do |example|
      submit_request(example.metadata)                                                                                            #
    end

    it "returns a #{metadata[:response][:code]} response" do |example|
      assert_response_matches_metadata(example.metadata, &block)
      example.instance_exec(response, &block) if block_given?
    end

    after do |example|
      body_parameter = example.metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:required] }

      if body_parameter && respond_to?(body_parameter[:name]) && example.metadata[:operation][:requestBody][:content]['application/json']
        # save response examples by default
        if example.metadata[:response][:examples].nil? || example.metadata[:response][:examples].empty?
          example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } unless response.body.to_s.empty?
        end

        # save request examples using the let(:param_name) { REQUEST_BODY_HASH } syntax in the test
        if response.code.to_s =~ /^2\d{2}$/
          example.metadata[:operation][:requestBody][:content]['application/json'] = { examples: {} } unless example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
          json_request_examples = example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
          json_request_examples[body_parameter[:name]] = { value: send(body_parameter[:name]) }

          example.metadata[:operation][:requestBody][:content]['application/json'][:examples] = json_request_examples
        end
      end

      self.class.merge_other_examples!(example.metadata) if example.metadata[:operation][:requestBody]

    end
  end
end
schema(value, content_type: 'application/json') click to toggle source
# File lib/open_api/rswag/specs/example_group_helpers.rb, line 158
def schema(value, content_type: 'application/json')
  content_hash = {content_type => {schema: value}}
  metadata[:response][:content] = content_hash
end