Skip to content

Commit

Permalink
Raise a specific exception on unsupported version
Browse files Browse the repository at this point in the history
When a version is found that is no longer supported, or never was
supported, an application may want to handle this differently.
Previously generic routing error exceptions were raised and the
exception message stated no match or version is deprecated. Having the
same exception made it difficult to determine if it was not supported
or invalid, also the unsupported routing error erroneously contained
the word deprecated.

The new approach is to raise a new typed exception when the version is
not supported. It will include the versioned request as part of the
exception to allow inspection in an error handler.

This should resolve the issue #24.
  • Loading branch information
bwillis committed Mar 1, 2014
1 parent 643c543 commit 2651420
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,8 @@ Bug Fixes:

Enhancements:

* Allow simpler handling of an unsupported versioned request by raising a custom error (issues #24 and #25)

## 2.0.0 (Feb 6, 2014)

[Full Changelog](https://github.com/bwillis/versioncake/compare/v1.3...v2.0)
Expand Down
26 changes: 26 additions & 0 deletions README.md
Expand Up @@ -281,6 +281,32 @@ end

When a client makes a request it will automatically receive the latest supported version of the view. The client can also request for a specific version by one of the strategies configured by ``view_version_extraction_strategy``.

### Unsupported Version Requests

If a client requests a version that is no longer supported (is not included in the `config.versioncake.supported_version_numbers`), a `VersionCake::UnsupportedVersionError` will be raised. This can be handled using Rails `rescue_from` to return app specific messages to the client.

```ruby
class ApplicationController < ActionController::Base

...

rescue_from VersionCake::UnsupportedVersionError, :with => :render_unsupported_version

private

def render_unsupported_version
headers['X-API-Version-Supported'] = 'false'
respond_to do |format|
format.json { render json: {message: "You requested an unsupported version (#{requested_version})"}, status: :unprocessable_entity }
end
end

...

end

```

## How to test

Testing can be painful but here are some easy ways to test different versions of your api using version cake.
Expand Down
1 change: 1 addition & 0 deletions lib/versioncake.rb
Expand Up @@ -6,6 +6,7 @@
require 'versioncake/strategies/request_parameter_strategy'
require 'versioncake/strategies/custom_strategy'

require 'versioncake/exceptions'
require 'versioncake/configuration'
require 'versioncake/controller_additions'
require 'versioncake/view_additions'
Expand Down
5 changes: 4 additions & 1 deletion lib/versioncake/controller_additions.rb
Expand Up @@ -33,8 +33,11 @@ def set_version(override_version=nil)
versioned_request = VersionCake::VersionedRequest.new(request, override_version)
@requested_version = versioned_request.extracted_version
@derived_version = versioned_request.version
@is_latest_version = versioned_request.is_latest_version?
if !versioned_request.is_version_supported?
raise UnsupportedVersionError.new('Unsupported version error')
end
@_lookup_context.versions = versioned_request.supported_versions
@is_latest_version = versioned_request.is_latest_version
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/versioncake/exceptions.rb
@@ -0,0 +1,6 @@
require 'action_controller/metal/exceptions'

module VersionCake
class UnsupportedVersionError < ::ActionController::RoutingError
end
end
27 changes: 15 additions & 12 deletions lib/versioncake/versioned_request.rb
@@ -1,16 +1,23 @@
module VersionCake
class VersionedRequest
attr_reader :version, :extracted_version, :is_latest_version
attr_reader :version, :extracted_version

def initialize(request, version_override=nil)
@version = version_override || extract_version(request)
@is_latest_version = @version == config.latest_version
derive_version(request, version_override)
end

def supported_versions
config.supported_versions(@version)
end

def is_latest_version?
@version == config.latest_version
end

def is_version_supported?
config.supports_version? @version
end

private

def config
Expand All @@ -26,16 +33,12 @@ def apply_strategies(request)
version
end

def extract_version(request)
@extracted_version = apply_strategies(request)
if @extracted_version.nil?
@version = config.default_version || config.latest_version
elsif config.supports_version? @extracted_version
@version = @extracted_version
elsif @extracted_version > config.latest_version
raise ActionController::RoutingError.new("No route match for version")
def derive_version(request, version_override)
if version_override
@version = version_override
else
raise ActionController::RoutingError.new("Version is deprecated")
@extracted_version = apply_strategies(request)
@version = @extracted_version || config.default_version || config.latest_version
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions test/functional/renders_controller_test.rb
Expand Up @@ -46,13 +46,13 @@ class RendersControllerTest < ActionController::TestCase
end

test "responds with 404 when the version is larger than the supported version" do
assert_raise ActionController::RoutingError do
assert_raise VersionCake::UnsupportedVersionError do
get :index, "api_version" => "4"
end
end

test "responds with 404 when the version is lower than the latest version, but not an available version" do
assert_raise ActionController::RoutingError do
assert_raise VersionCake::UnsupportedVersionError do
get :index, "api_version" => "0"
end
end
Expand Down
10 changes: 3 additions & 7 deletions test/unit/versioned_request_test.rb
Expand Up @@ -15,23 +15,19 @@ class VersionedRequestTest < ActiveSupport::TestCase

test "a request for a version that is higher than the latest version raises an error" do
VersionCake::VersionedRequest.any_instance.stubs(:apply_strategies => 99)
assert_raise ActionController::RoutingError do
VersionCake::VersionedRequest.new(stub())
end
assert !VersionCake::VersionedRequest.new(stub()).is_version_supported?
end

test "a request for a deprecated version raises an exception" do
VersionCake::VersionedRequest.any_instance.stubs(:apply_strategies => 2)
VersionCake::Configuration.any_instance.stubs(:supports_version? => false)
assert_raise ActionController::RoutingError do
VersionCake::VersionedRequest.new(stub())
end
assert !VersionCake::VersionedRequest.new(stub()).is_version_supported?
end

test "has a method to determine if requesting the latest version" do
VersionCake::VersionedRequest.any_instance.stubs(:apply_strategies => nil)
versioned_request = VersionCake::VersionedRequest.new(stub())
assert versioned_request.is_latest_version
assert versioned_request.is_latest_version?
end

test "has a method to retrieve the extracted version" do
Expand Down

0 comments on commit 2651420

Please sign in to comment.