From 4838523a340337ed68c758f4ecbd09b68bca639f Mon Sep 17 00:00:00 2001 From: Ben Willis Date: Sun, 3 Apr 2016 14:43:46 -0700 Subject: [PATCH 1/6] Dependency injection for config --- lib/versioncake/test_helpers.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/versioncake/test_helpers.rb b/lib/versioncake/test_helpers.rb index c0a3da8..d6a6272 100644 --- a/lib/versioncake/test_helpers.rb +++ b/lib/versioncake/test_helpers.rb @@ -2,8 +2,8 @@ module VersionCake module TestHelpers # Test helper the mimics the middleware because we do not # have middleware during tests. - def set_request_version(resource, version) - service = VersionCake::VersionContextService.new(VersionCake.config) + def set_request_version(resource, version, config=VersionCake.config) + service = VersionCake::VersionContextService.new(config) @request.env['versioncake.context'] = service.create_context resource, version end @@ -11,4 +11,4 @@ def set_version_context(status, resource=nil, version=nil) @request.env['versioncake.context'] = VersionCake::VersionContext.new(version, resource, status) end end -end \ No newline at end of file +end From 85303d6b78bd5bcc36a543eb006bad9c5739bfc6 Mon Sep 17 00:00:00 2001 From: Ben Willis Date: Sun, 3 Apr 2016 15:56:28 -0700 Subject: [PATCH 2/6] Allow configuration `missing_version` to be set to `unversioned_template` `missing_version` now configures two options, one for `default_version` and one for `missing_version_use_unversioned_template`. `default_version` is used in the Rack layer to apply a version for a missing version. `missing_version_use_unversioned_template` is used at the rails layer to not fail when the request is missing a version and no default version is set. --- lib/generators/templates/versioncake.rb | 14 ++++++++++++-- lib/versioncake/configuration.rb | 18 ++++++++++++++++-- spec/unit/configuration_spec.rb | 16 ++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/generators/templates/versioncake.rb b/lib/generators/templates/versioncake.rb index 1a1b392..d49f340 100644 --- a/lib/generators/templates/versioncake.rb +++ b/lib/generators/templates/versioncake.rb @@ -29,8 +29,18 @@ # ``` # config.extraction_strategy = [:http_accept_parameter, :http_header, :request_parameter, :path_parameter, :query_parameter] - # Version when no version in present in the request. If none is specified then it will error - config.missing_version = 5 + # Missing Version + # What to use when no version in present in the request. + # + # Defaults to `:unversioned_template` + # + # Integer value: + # the version number to use + # + # `:unversioned_template` value: + # If you are using `rails_view_versioning` this will render the "base template" aka + # the template without a version number. + # config.missing_version = :unversioned_template # Set the version key that clients will send example: `API-VERSION: 5`, api_version=2 # config.version_key = 'api_version' diff --git a/lib/versioncake/configuration.rb b/lib/versioncake/configuration.rb index ae36d57..656a47b 100644 --- a/lib/versioncake/configuration.rb +++ b/lib/versioncake/configuration.rb @@ -7,13 +7,16 @@ class Configuration SUPPORTED_VERSIONS_DEFAULT = (1..10) VERSION_KEY_DEFAULT = 'api_version' - attr_reader :extraction_strategies, :response_strategies, :supported_version_numbers, :versioned_resources + attr_reader :extraction_strategies, :response_strategies, :supported_version_numbers, + :versioned_resources, :default_version, :missing_version_use_unversioned_template attr_accessor :missing_version, :version_key, :rails_view_versioning def initialize @versioned_resources = [] @version_key = VERSION_KEY_DEFAULT @rails_view_versioning = true + @missing_version_use_unversioned_template = true + @default_version = nil self.supported_version_numbers = SUPPORTED_VERSIONS_DEFAULT self.extraction_strategy = [ :http_accept_parameter, @@ -25,6 +28,17 @@ def initialize self.response_strategy = [] end + def missing_version=(val) + @missing_version = val + if @missing_version == :unversioned_template + @missing_version_use_unversioned_template = true + @default_version = nil + else + @missing_version_use_unversioned_template = false + @default_version = val + end + end + def extraction_strategy=(val) @extraction_strategies = [] Array.wrap(val).each do |configured_strategy| @@ -76,4 +90,4 @@ def resource(regex, obsolete, unsupported, supported) @resources << VersionCake::VersionedResource.new(regex, obsolete, unsupported, supported) end end -end \ No newline at end of file +end diff --git a/spec/unit/configuration_spec.rb b/spec/unit/configuration_spec.rb index 624c774..9c454dc 100644 --- a/spec/unit/configuration_spec.rb +++ b/spec/unit/configuration_spec.rb @@ -60,6 +60,22 @@ it { expect(config.latest_version).to eq 54 } end + context '#missing_version' do + before { config.missing_version = 5 } + + it { expect(config.missing_version).to eq 5 } + it { expect(config.default_version).to eq 5 } + it { expect(config.missing_version_use_unversioned_template).to eq false } + + context 'when set to use the base template' do + before { config.missing_version = :unversioned_template } + + it { expect(config.missing_version).to eq :unversioned_template } + it { expect(config.default_version).to be_nil } + it { expect(config.missing_version_use_unversioned_template).to eq true } + end + end + context 'by default' do it 'has all extraction strategies' do expect(config.extraction_strategies.map(&:class)).to match_array( From fbe73169308b8e7a52838390e7be4d71557b1c39 Mon Sep 17 00:00:00 2001 From: Ben Willis Date: Sun, 3 Apr 2016 15:56:51 -0700 Subject: [PATCH 3/6] VersionContextService now to use `default_version` --- lib/versioncake/version_context_service.rb | 2 +- spec/unit/version_context_service_spec.rb | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/versioncake/version_context_service.rb b/lib/versioncake/version_context_service.rb index e4e8da5..f33ae53 100644 --- a/lib/versioncake/version_context_service.rb +++ b/lib/versioncake/version_context_service.rb @@ -3,7 +3,7 @@ class VersionContextService def initialize(config) @versioned_resources = config.versioned_resources - @default_version = config.missing_version + @default_version = config.default_version @strategies = config.extraction_strategies end diff --git a/spec/unit/version_context_service_spec.rb b/spec/unit/version_context_service_spec.rb index e8abb88..3f10871 100644 --- a/spec/unit/version_context_service_spec.rb +++ b/spec/unit/version_context_service_spec.rb @@ -21,12 +21,13 @@ let(:config) do double('config', versioned_resources: [resource_user, resource_all], - missing_version: 9, + default_version: default_version, extraction_strategies: [ VersionCake::CustomStrategy.new(lambda{ |req| req.version }) ] ) end + let(:default_version) { 6 } let(:service) { described_class.new(config) } describe '#create_context_from_request' do @@ -52,6 +53,22 @@ it { expect(context.resource).to eq resource_user } it { expect(context.result).to eq :obsolete } end + + context 'for a missing version' do + let(:request) { double(version: nil, path: 'users/123') } + + it { expect(context.version).to eq 6 } + it { expect(context.resource).to eq resource_user } + it { expect(context.result).to eq :supported } + + context 'when no default version is configured' do + let(:default_version) { nil } + + it { expect(context.version).to eq nil } + it { expect(context.resource).to eq resource_user } + it { expect(context.result).to eq :no_version } + end + end end describe '#create_context' do From 3762bd221dcd170f727e6d00f9bcda45a95039ae Mon Sep 17 00:00:00 2001 From: Ben Willis Date: Sun, 3 Apr 2016 15:57:55 -0700 Subject: [PATCH 4/6] Rails controller level to use `missing_version_use_unversioned_template` When this option is set and there is no version, avoid failing hard and ensure we set the lookup context version to nil so the base template will be found. --- lib/versioncake/controller_additions.rb | 30 ++++++++++++++----- .../controller/renders_controller_spec.rb | 25 +++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/versioncake/controller_additions.rb b/lib/versioncake/controller_additions.rb index a8f4cfa..3e5dfb4 100644 --- a/lib/versioncake/controller_additions.rb +++ b/lib/versioncake/controller_additions.rb @@ -45,17 +45,31 @@ def version_context # @return No explicit return, but several attributes are exposed def check_version!(override_version=nil) return unless version_context - - case version_context.result - when :version_invalid, :version_too_high, :version_too_low, :unknown - raise UnsupportedVersionError.new('Unsupported version error') - when :obsolete - raise ObsoleteVersionError.new('The version given is obsolete') - when :no_version + + check_for_version_errors!(version_context.result) + configure_rails_view_versioning(version_context) + end + + def check_for_version_errors!(result) + case result + when :version_invalid, :version_too_high, :version_too_low, :unknown + raise UnsupportedVersionError.new('Unsupported version error') + when :obsolete + raise ObsoleteVersionError.new('The version given is obsolete') + when :no_version + unless VersionCake.config.missing_version_use_unversioned_template raise MissingVersionError.new('No version was given') + end end + end + + def configure_rails_view_versioning(version_context) + return unless VersionCake.config.rails_view_versioning - if VersionCake.config.rails_view_versioning + if version_context.result == :no_version && + VersionCake.config.missing_version_use_unversioned_template + @_lookup_context.versions = nil + else @_lookup_context.versions = version_context.available_versions.map { |n| :"v#{n}" } end end diff --git a/spec/integration/controller/renders_controller_spec.rb b/spec/integration/controller/renders_controller_spec.rb index 94c90d8..7048a28 100644 --- a/spec/integration/controller/renders_controller_spec.rb +++ b/spec/integration/controller/renders_controller_spec.rb @@ -6,7 +6,8 @@ context '#index' do render_views - before { set_request_version 'renders', request_version } + let(:config) { VersionCake.config } + before { set_request_version 'renders', request_version, config } before { response_body } context 'when requesting the non latest version' do @@ -36,6 +37,28 @@ it { expect(response_body).to eq 'template v2' } end + context 'when requesting without a version' do + context 'and missing version is configured to unversioned_template' do + let(:request_version) { nil } + + # TODO: Restructure + # Need a better way to stub/unstub the `VersionCake.config` that generates + # the context and is used in the controller. + let(:config) do + VersionCake.config.tap do |config| + config.missing_version = :unversioned_template + end + end + after { VersionCake.config.missing_version = nil } + # /TODO + + it { expect(controller.request_version).to eq nil } + it { expect(controller.is_latest_version?).to be_falsey } + it { expect(controller.is_deprecated_version?).to be_falsey } + it { expect(response_body).to eq 'base template' } + end + end + context '#set_version' do let(:request_options) { { 'override_version' => 2 } } let(:request_version) { 3 } From 2d8eec52fca62ac7954023981ad534f553b1b4bb Mon Sep 17 00:00:00 2001 From: Ben Willis Date: Sun, 3 Apr 2016 15:58:07 -0700 Subject: [PATCH 5/6] Regression spec to make sure I didn't break anything --- .../rack/middleware_regression_spec.rb | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/spec/integration/rack/middleware_regression_spec.rb b/spec/integration/rack/middleware_regression_spec.rb index 3b1c67a..dc90790 100644 --- a/spec/integration/rack/middleware_regression_spec.rb +++ b/spec/integration/rack/middleware_regression_spec.rb @@ -3,25 +3,27 @@ describe VersionCake::Rack::Middleware do let(:app) do + _config = config rack = Rack::Builder.new do - config = VersionCake::Configuration.new - config.resources { |r| r.resource %r{.*}, [], [], (1..5) } - use VersionCake::Rack::Middleware, config - run lambda { |env| [ 200, {},[ env['versioncake.context'].version ] ] } + use VersionCake::Rack::Middleware, _config + run lambda { |env| [ 200, {},[ "version is #{env['versioncake.context'].version}" ] ] } end Rack::MockRequest.new(rack) end + let(:config) do + config = VersionCake::Configuration.new + config.resources { |r| r.resource %r{.*}, [], [], (1..5) } + config + end test_cases = YAML.load(File.open('./spec/fixtures/test_cases.yml')) test_cases.each do |test_case| context 'for a test case' do - - let(:data) { test_case['request'] || {} } let(:method) { (data['method'] || 'get').to_sym } let(:headers) { data['headers'] || {} } let(:params) { data['params'] || {} } - let(:test_response) { test_case['response'] } + let(:test_response) { "version is #{test_case['response']}" } it "test yml test cases" do begin @@ -40,4 +42,20 @@ def custom_message(headers, params, method, actual_response, expected_response) data << "params:#{params}" if params "Expected #{data.join(',')} with method #{method} to yield '#{expected_response}', but got '#{actual_response}'" end + + context 'when configured with unversioned template' do + let(:config) do + config = VersionCake::Configuration.new + config.resources { |r| r.resource %r{.*}, [], [], (1..5) } + config.missing_version = :unversioned_template + config + end + + context 'and the request does not contain a version' do + it 'does not include a version (rails will convert nil => unversioned template)' do + _response_status, _response_headers, response = app.request('get', '/renders') + expect(response.body).to eq 'version is ' # nil + end + end + end end From 5e24766e3a9523dfb068e7970a1b05c234c5e484 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 5 Apr 2016 19:55:54 -0700 Subject: [PATCH 6/6] Update CHANGELOG.md Updating change log from latest bug fix --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da8f7c4..be831d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ ## Unreleased Changes -[Full Changelog](https://github.com/bwillis/versioncake/compare/v3.0...master) +[Full Changelog](https://github.com/bwillis/versioncake/compare/v3.1...master) Bug Fixes: -* None +* Deprecated versions would not render properly (#47) Enhancements: