Skip to content

Commit

Permalink
Add #select_example_response config option.
Browse files Browse the repository at this point in the history
  • Loading branch information
myronmarston committed Oct 18, 2012
1 parent 1ca7799 commit c9c1fd5
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 36 deletions.
15 changes: 15 additions & 0 deletions README.md
Expand Up @@ -231,6 +231,21 @@ Interpol.default_configuration do |config|
example.data["current_url"] = Rack::Request.new(request_env).url example.data["current_url"] = Rack::Request.new(request_env).url
end end


# Sets a callback that will be used to determine which example
# to return from the stub app. If you provide an endpoint name,
# the block will apply only to requests to that endpoint.
# If no name is provided, the block will set the default selector
# logic. By default, if this config is not set, interpol will use
# the first example.
#
# Used by Interpol::StubApp.
config.select_example_response('some-endpoint') do |endpoint_def, request_env|
endpoint_def.examples[3]
end
config.select_example_response do |endpoint_def, request_env|
endpoint_def.examples.first
end

# Determines what to do when Interpol::Sinatra::RequestParamsParser # Determines what to do when Interpol::Sinatra::RequestParamsParser
# detects invalid path or query parameters based on their schema # detects invalid path or query parameters based on their schema
# definitions. This block will be eval'd in the context of your # definitions. This block will be eval'd in the context of your
Expand Down
21 changes: 21 additions & 0 deletions lib/interpol/configuration.rb
Expand Up @@ -141,6 +141,19 @@ def filter_example_data(&block)
filter_example_data_blocks << block filter_example_data_blocks << block
end end


def select_example_response(endpoint_name = nil, &block)
if endpoint_name
named_example_selectors[endpoint_name] = block
else
named_example_selectors.default = block
end
end

def example_response_for(endpoint_def, env)
selector = named_example_selectors[endpoint_def.endpoint_name]
selector.call(endpoint_def, env)
end

def self.default def self.default
@default ||= Configuration.new @default ||= Configuration.new
end end
Expand Down Expand Up @@ -187,6 +200,10 @@ def rack_json_response(status, hash)
'Content-Length' => json.bytesize }, [json]] 'Content-Length' => json.bytesize }, [json]]
end end


def named_example_selectors
@named_example_selectors ||= {}
end

def register_default_callbacks def register_default_callbacks
request_version do request_version do
raise ConfigurationError, "request_version has not been configured" raise ConfigurationError, "request_version has not been configured"
Expand Down Expand Up @@ -229,6 +246,10 @@ def register_default_callbacks
on_invalid_sinatra_request_params do |error| on_invalid_sinatra_request_params do |error|
halt 400, JSON.dump(:error => error.message) halt 400, JSON.dump(:error => error.message)
end end

select_example_response do |endpoint_def, _|
endpoint_def.examples.first
end
end end
end end
end end
Expand Down
8 changes: 0 additions & 8 deletions lib/interpol/endpoint.rb
Expand Up @@ -68,14 +68,6 @@ def find_definitions(version, message_type, &block)
@definitions_hash.fetch([message_type, version], &block) @definitions_hash.fetch([message_type, version], &block)
end end


def find_example_for!(version, message_type)
find_definition!(version, message_type).examples.first
end

def find_example_status_code_for!(version)
find_definition!(version, 'response').example_status_code
end

def available_request_versions def available_request_versions
available_versions_matching &:request? available_versions_matching &:request?
end end
Expand Down
23 changes: 12 additions & 11 deletions lib/interpol/stub_app.rb
Expand Up @@ -44,24 +44,25 @@ def build


def endpoint_definition(endpoint) def endpoint_definition(endpoint)
lambda do lambda do
example, version = settings. endpoint_def = settings.stub_app_builder.endpoint_def_for(endpoint, self)
stub_app_builder. example = settings.stub_app_builder.example_for(endpoint_def, self)
example_and_version_for(endpoint, self)
example.validate! if settings.perform_validations? example.validate! if settings.perform_validations?
status endpoint.find_example_status_code_for!(version) status endpoint_def.example_status_code
JSON.dump(example.data) JSON.dump(example.data)
end end
end end


def example_and_version_for(endpoint, app) def endpoint_def_for(endpoint, app)
version = config.response_version_for(app.request.env, endpoint) version = config.response_version_for(app.request.env, endpoint)
example = endpoint.find_example_for!(version, 'response') endpoint_def = endpoint.find_definition!(version, 'response')
rescue NoEndpointDefinitionFoundError rescue NoEndpointDefinitionFoundError
config.sinatra_request_version_unavailable(app, version, config.sinatra_request_version_unavailable \
endpoint.available_response_versions) app, version, endpoint.available_response_versions
else end
example = example.apply_filters(config.filter_example_data_blocks, app.request.env)
return example, version def example_for(endpoint_def, app)
example = config.example_response_for(endpoint_def, app.request.env)
example.apply_filters(config.filter_example_data_blocks, app.request.env)
end end
end end
end end
Expand Down
15 changes: 0 additions & 15 deletions spec/unit/interpol/endpoint_spec.rb
Expand Up @@ -120,21 +120,6 @@ def build_hash(hash = {})
end end
end end


describe "#find_example_for!" do
let(:hash) { build_hash('definitions' => definitions_array) }
let(:endpoint) { Endpoint.new(hash) }

it 'returns an example for the requested version' do
endpoint.find_example_for!('1.2', 'response').data.should eq('e1')
end

it 'raises an error when given a version it does not have' do
expect {
endpoint.find_example_for!('2.1', 'response')
}.to raise_error(NoEndpointDefinitionFoundError)
end
end

describe '#route_matches?' do describe '#route_matches?' do
def endpoint(route) def endpoint(route)
hash = build_hash('route' => route) hash = build_hash('route' => route)
Expand Down
79 changes: 77 additions & 2 deletions spec/unit/interpol/stub_app_spec.rb
Expand Up @@ -36,10 +36,35 @@ module Interpol
EOF EOF
end end


let_without_indentation(:another_definition_yml) do <<-EOF
---
name: another_endpoint
route: /another-endpoint
method: GET
definitions:
- versions: ["1.0"]
status_codes: ['200']
message_type: response
schema:
type: object
properties:
example_num: { type: integer }
examples:
- example_num: 0
- example_num: 1
- versions: ["1.0"]
message_type: request
schema: {}
examples: []
EOF
end

let(:endpoint) { Endpoint.new(YAML.load endpoint_definition_yml) } let(:endpoint) { Endpoint.new(YAML.load endpoint_definition_yml) }
let(:another_endpoint) { Endpoint.new(YAML.load another_definition_yml) }

let(:default_config) do let(:default_config) do
lambda do |config| lambda do |config|
config.endpoints = [endpoint] config.endpoints = [endpoint, another_endpoint]


unless response_version_configured?(config) # allow default config to take precedence unless response_version_configured?(config) # allow default config to take precedence
config.response_version { |env, _| env.fetch('HTTP_RESPONSE_VERSION') } config.response_version { |env, _| env.fetch('HTTP_RESPONSE_VERSION') }
Expand Down Expand Up @@ -95,6 +120,56 @@ def parsed_body
last_response.should be_ok last_response.should be_ok
end end


it 'uses the select_example_response callback to select which example gets returned' do
Interpol.default_configuration do |c|
c.select_example_response do |endpoint_def, request|
endpoint_def.examples.last
end
end

header 'Response-Version', '1.0'
get '/users/3/projects'
parsed_body.should include('name' => 'some other project')
end

it 'can select an example based on the request' do
Interpol.default_configuration do |c|
c.select_example_response do |endpoint_def, env|
index = Integer(env.fetch('HTTP_INDEX'))
endpoint_def.examples[index]
end
end

header 'Response-Version', '1.0'

header 'Index', '0'
get '/another-endpoint'
parsed_body.should include('example_num' => 0)

header 'Index', '1'
get '/another-endpoint'
parsed_body.should include('example_num' => 1)
end

it 'can have different example selection logic for a particular endpoint' do
Interpol.default_configuration do |c|
c.select_example_response do |endpoint_def, request|
endpoint_def.examples.last
end

c.select_example_response 'another_endpoint' do |endpoint_def, request|
endpoint_def.examples.first
end
end

header 'Response-Version', '1.0'
get '/users/3/projects'
parsed_body.should include('name' => 'some other project')

get '/another-endpoint'
parsed_body.should include('example_num' => 0)
end

it 'uses any provided filters to modify the example data' do it 'uses any provided filters to modify the example data' do
app.settings.stub_app_builder.config.filter_example_data do |example, request_env| app.settings.stub_app_builder.config.filter_example_data do |example, request_env|
example.data["name"] += " for #{request_env["REQUEST_METHOD"]}" example.data["name"] += " for #{request_env["REQUEST_METHOD"]}"
Expand Down Expand Up @@ -144,7 +219,7 @@ def parsed_body
end end


let(:endpoint_example) do let(:endpoint_example) do
endpoint.find_example_for!('1.0', 'response') endpoint.find_definition!('1.0', 'response').examples.first
end end


it 'performs validations by default' do it 'performs validations by default' do
Expand Down

0 comments on commit c9c1fd5

Please sign in to comment.