sinatra-rest-base 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +77 -0
- data/Rakefile +2 -0
- data/lib/rest/application.rb +42 -0
- data/lib/rest/application_helper.rb +65 -0
- data/lib/rest/authentication/authentication_base.rb +9 -0
- data/lib/rest/authentication/basic_authentication.rb +15 -0
- data/lib/rest/authentication.rb +7 -0
- data/lib/rest/error_handeling.rb +17 -0
- data/lib/rest/error_status_handeling.rb +23 -0
- data/lib/rest/exceptions/invalid_request.rb +12 -0
- data/lib/rest/exceptions.rb +6 -0
- data/lib/rest/version.rb +3 -0
- data/lib/rest.rb +5 -0
- data/sinatra-rest-base.gemspec +32 -0
- data/spec/fixtures/test_application.rb +11 -0
- data/spec/fixtures/test_end_points.rb +60 -0
- data/spec/fixtures/test_errors_end_points.rb +34 -0
- data/spec/lib/rest/application_spec.rb +184 -0
- data/spec/lib/rest/authentication/authentication_base_spec.rb +11 -0
- data/spec/lib/rest/authentication/basic_authentication_spec.rb +29 -0
- data/spec/lib/rest/error_handeling_spec.rb +24 -0
- data/spec/lib/rest/error_status_handeling_spec.rb +104 -0
- data/spec/spec_helper.rb +21 -0
- metadata +220 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9af7fb5e0df73f24103bb5b39ea04ff158da525
|
4
|
+
data.tar.gz: 163aa0d3b19c1602dc156774e8ed3d78068e9471
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ad4876e275d150164b2b7deabf58de1b1ee57ada749377740ff861cb3548d02a9944c9c1eddc90960d4e9d890fcd9fb9a93905dcd6ef1a5bf3c5474ab8f21829
|
7
|
+
data.tar.gz: 9a912cb5fff19ba832be1bb90bc5e7d9d4346569fe944b7f253e9f862e4940b9bfd61e8de4ff4067f5a7c420b23a009df61b3855138a8908a498053751e244d2
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 bmills
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Rest
|
2
|
+
|
3
|
+
Core base classes for quick configuration of Sinatra for RESTful Services
|
4
|
+
|
5
|
+
#### What Does the Core Cover
|
6
|
+
|
7
|
+
* Adherence to [API Standards] (https://github.com/RESTful-Forest/Sinatra-Rest-Base/wiki/RESTful-API-Standards)
|
8
|
+
* Basic Configuration
|
9
|
+
* Storing the IO String Request Body into a global hash 'request_body'
|
10
|
+
* Processing the response body into the proper accepted mime type (application/json, text/xml, ect.) depending on the HTTP_ACCEPT header.
|
11
|
+
* Defaults to application/json
|
12
|
+
* Processing the version of the application being accessed via HTTP_ACCEPT header
|
13
|
+
* Basic Error Handeling
|
14
|
+
* Handles Authentication Errors, StandardError, Error status codes 400..510
|
15
|
+
* Implements Basic Error Classes
|
16
|
+
* InvalidRequest
|
17
|
+
* More to come soon!
|
18
|
+
* Simple authentication setup
|
19
|
+
* Provides a base authentication class to inherit from and create a quick authentication process.
|
20
|
+
* Provides an easy authentication setup through the configuration setup of Sinatra.
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Add this line to your application's Gemfile:
|
25
|
+
|
26
|
+
gem 'sinatra-rest-base'
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
|
34
|
+
$ gem install sinatra-rest-base
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
#### Setting Up the Application
|
39
|
+
|
40
|
+
Normal Sinatra Appication inherit from either Sinatra::Application or Sinatra::Base. With sinatra-rest-base you just need to inherit from Rest::Application.
|
41
|
+
|
42
|
+
In your main application file, do the following:
|
43
|
+
|
44
|
+
```
|
45
|
+
require 'rest'
|
46
|
+
|
47
|
+
class SinatraApp < Rest::Application
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
then in any other partial class file of the main application file, you just have to name the class like follow:
|
52
|
+
|
53
|
+
```
|
54
|
+
require 'rest'
|
55
|
+
|
56
|
+
class SinatraApp
|
57
|
+
|
58
|
+
get '/' do
|
59
|
+
{
|
60
|
+
:result => "This is so easy now!"
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Bam, that easy!
|
68
|
+
|
69
|
+
## [Documentation Wiki] (https://github.com/RESTful-Forest/Sinatra-Rest-Base/wiki/Documentation)
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
1. Fork it ( https://github.com/[my-github-username]/sintara-rest-base/fork )
|
74
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
75
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
76
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
77
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'json'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/hash'
|
5
|
+
require 'rest/authentication'
|
6
|
+
|
7
|
+
module Rest
|
8
|
+
class Application < Sinatra::Base
|
9
|
+
attr_reader :request_body
|
10
|
+
attr_reader :request_version
|
11
|
+
attr_reader :errored
|
12
|
+
|
13
|
+
configure do
|
14
|
+
disable :raise_errors
|
15
|
+
disable :show_exceptions
|
16
|
+
set :api_version, 1
|
17
|
+
set :authentication, Rest::Authentication::BasicAuthentication.new([])
|
18
|
+
disable :header_versioning
|
19
|
+
end
|
20
|
+
|
21
|
+
set(:version) do |value|
|
22
|
+
condition do
|
23
|
+
has_version?("version=#{value}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
status 200
|
29
|
+
|
30
|
+
set_api_version if settings.header_versioning
|
31
|
+
set_request_body
|
32
|
+
error 401 unless authenticated?
|
33
|
+
end
|
34
|
+
|
35
|
+
after do
|
36
|
+
response.body = nil if response.body.is_a?(Array) && response.body.length == 1 && response.body[0].empty?
|
37
|
+
response.body = create_response()
|
38
|
+
|
39
|
+
response.headers['Content-Type'] += ";version=#{@request_version}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Rest
|
2
|
+
class Application < Sinatra::Base
|
3
|
+
def authenticated?
|
4
|
+
authorized = true
|
5
|
+
authorized = settings.authentication.authorized?(request, @env['HTTP_AUTHORIZATION']) unless settings.authentication.nil?
|
6
|
+
authorized
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_version?(version)
|
10
|
+
@env['HTTP_ACCEPT'] = "*/*" if @env['HTTP_ACCEPT'].nil?
|
11
|
+
@env['HTTP_ACCEPT'].include?(version)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_version
|
15
|
+
matchdata = @env['HTTP_ACCEPT'].to_s.scan(/(version)=(\d*)/)
|
16
|
+
matchdata[0][1].delete(',').to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_api_version
|
20
|
+
@env['HTTP_ACCEPT'] += ";version=#{settings.api_version}" unless has_version?("version=")
|
21
|
+
@env['sinatra.accept'] = nil
|
22
|
+
@request_version = get_version
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_request_body
|
26
|
+
read_body = request.body.read
|
27
|
+
|
28
|
+
if read_body.empty?
|
29
|
+
@request_body = {}
|
30
|
+
elsif request.media_type.downcase == 'text/xml'
|
31
|
+
@request_body = Hash.from_xml(read_body)
|
32
|
+
@request_body = Hash[@request_body.map{ |k, v| [k.to_sym, v] }]
|
33
|
+
@request_body = @request_body[:request]
|
34
|
+
else
|
35
|
+
@request_body = JSON.parse(read_body, :symbolize_names => true)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_response()
|
40
|
+
content_type :json
|
41
|
+
content_type :xml if !request.accept?('*/*') && request.accept?('text/xml')
|
42
|
+
|
43
|
+
if (response.status >= 400)
|
44
|
+
error_hash = response.body
|
45
|
+
error_hash = [error_hash] if error_hash.is_a?(String)
|
46
|
+
|
47
|
+
response.body = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
response_hash = {
|
51
|
+
:errors => error_hash,
|
52
|
+
:results => response.body
|
53
|
+
}
|
54
|
+
|
55
|
+
return '' if empty_body_status_code? && (response.body.empty?)
|
56
|
+
return response_hash.to_xml(:root => :response) if response.headers['Content-Type'].start_with? 'application/xml;'
|
57
|
+
response_hash.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
def empty_body_status_code?
|
61
|
+
empty_body_status_codes = [201, 204, 304]
|
62
|
+
response != nil && empty_body_status_codes.include?(response.status)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Rest
|
2
|
+
module Authentication
|
3
|
+
class BasicAuthentication < AuthenticationBase
|
4
|
+
def initialize(keys)
|
5
|
+
@valid_keys = keys
|
6
|
+
end
|
7
|
+
|
8
|
+
def authorized?(request, key)
|
9
|
+
authorized = true
|
10
|
+
authorized = @valid_keys.include?(key) unless @valid_keys.length == 0
|
11
|
+
authorized
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'rest/exceptions'
|
3
|
+
|
4
|
+
module Rest
|
5
|
+
class Application < Sinatra::Base
|
6
|
+
error Rest::Exceptions::InvalidRequest do
|
7
|
+
cur_error = env["sinatra.error"]
|
8
|
+
errors = [cur_error.message]
|
9
|
+
|
10
|
+
cur_error.invalid_params.each { |param, message|
|
11
|
+
errors << "#{param.to_s} is not valid: #{message}"
|
12
|
+
}
|
13
|
+
|
14
|
+
errors
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rest
|
2
|
+
class Application < Sinatra::Base
|
3
|
+
not_found do
|
4
|
+
response.body = 'Endpoint not found'
|
5
|
+
end
|
6
|
+
|
7
|
+
error 401 do
|
8
|
+
response.body = 'Unauthorized Access'
|
9
|
+
create_response()
|
10
|
+
end
|
11
|
+
|
12
|
+
error 404 do
|
13
|
+
response.body = "Endpoint not found"
|
14
|
+
create_response()
|
15
|
+
end
|
16
|
+
|
17
|
+
error 500 do
|
18
|
+
return @env['sinatra.error'].message unless @env['sinatra.error'].nil?
|
19
|
+
response.body = 'Internal Error'
|
20
|
+
create_response()
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rest/version.rb
ADDED
data/lib/rest.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rest/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sinatra-rest-base"
|
8
|
+
spec.version = Rest::VERSION
|
9
|
+
spec.authors = ["bmills"]
|
10
|
+
spec.email = ["brandon.mills@careerbuilder.com"]
|
11
|
+
spec.summary = %q{Base class for quickly setting up RESTful services through Sinatra.}
|
12
|
+
spec.description = %q{Base class for quickly setting up RESTful services through Sinatra.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "json", "~> 1.8"
|
22
|
+
spec.add_dependency "sinatra", "~> 1.4"
|
23
|
+
spec.add_dependency "thin", "~> 1.6"
|
24
|
+
spec.add_dependency "activesupport", "~> 4.1"
|
25
|
+
spec.add_dependency "builder", "~> 3.2"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rack-test", "~> 0.6"
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
+
spec.add_development_dependency "simplecov", "~> 0.8"
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Rest
|
2
|
+
module Test
|
3
|
+
class TestApplication
|
4
|
+
get '/', :version => 1 do
|
5
|
+
{
|
6
|
+
:gotten => "success"
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
get '/', :version => 2 do
|
11
|
+
{
|
12
|
+
:gotten => "success 2"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/empty', :version => 1 do
|
17
|
+
""
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/created', :version => 1 do
|
21
|
+
status 201
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/nocontent', :version => 1 do
|
25
|
+
status 204
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/notmodified', :version => 1 do
|
29
|
+
status 304
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/created_with_body', :version => 1 do
|
33
|
+
status 201
|
34
|
+
{ :created => true }
|
35
|
+
end
|
36
|
+
|
37
|
+
post '/', :version => 1 do
|
38
|
+
{
|
39
|
+
:posted => request_body
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
put '/', :version => 1 do
|
44
|
+
{
|
45
|
+
:updated => request_body
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
delete '/', :version => 1 do
|
50
|
+
{
|
51
|
+
:deleted => request_body
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
delete '/nocontent', :version => 1 do
|
56
|
+
status 204
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rest
|
2
|
+
module Test
|
3
|
+
class TestApplication
|
4
|
+
get '/invalid_request', :version => 1 do
|
5
|
+
param_errors = {
|
6
|
+
:param1 => "Must be a String",
|
7
|
+
:param2 => "Must be an Integer"
|
8
|
+
}
|
9
|
+
|
10
|
+
raise Rest::Exceptions::InvalidRequest.new(param_errors)
|
11
|
+
end
|
12
|
+
|
13
|
+
get '/unauthorized', :version => 1 do
|
14
|
+
error 401
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/internal_error', :version => 1 do
|
18
|
+
error 500
|
19
|
+
end
|
20
|
+
|
21
|
+
get '/internal_error_raised', :version => 1 do
|
22
|
+
raise StandardError.new("Raised Internal Error")
|
23
|
+
end
|
24
|
+
|
25
|
+
get '/default_error_handeling', :version => 1 do
|
26
|
+
error 400, "This is the default error handler"
|
27
|
+
end
|
28
|
+
|
29
|
+
get '/hashed_error_handeling', :version => 1 do
|
30
|
+
error 400, { :error => "This is the hashed error handler" }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rest::Application do
|
4
|
+
before(:each) do
|
5
|
+
@headers = { 'HTTP_AUTHORIZATION' => 'key1' }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe :version do
|
9
|
+
it "should set the version to latest when not given" do
|
10
|
+
expected_version = "version=1"
|
11
|
+
|
12
|
+
get '/', {}, @headers
|
13
|
+
|
14
|
+
expect(last_request.env["HTTP_ACCEPT"].include? expected_version).to be_truthy
|
15
|
+
expect(last_response.headers["Content-Type"].include? expected_version).to be_truthy
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should get the version 1 version" do
|
19
|
+
expected_version = "version=1"
|
20
|
+
@headers['HTTP_ACCEPT'] = "application/json;#{expected_version}"
|
21
|
+
|
22
|
+
get '/', {}, @headers
|
23
|
+
|
24
|
+
expect(last_request.env["HTTP_ACCEPT"].include? expected_version).to be_truthy
|
25
|
+
expect(last_response.headers["Content-Type"].include? expected_version).to be_truthy
|
26
|
+
expect(last_response.body).to eq({:errors => nil, :results => { :gotten => "success" }}.to_json)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should get the version 2 version" do
|
30
|
+
expected_version = "version=2"
|
31
|
+
@headers['HTTP_ACCEPT'] = expected_version
|
32
|
+
|
33
|
+
get '/', {}, @headers
|
34
|
+
|
35
|
+
expect(last_request.env["HTTP_ACCEPT"].include? expected_version).to be_truthy
|
36
|
+
expect(last_response.headers["Content-Type"].include? expected_version).to be_truthy
|
37
|
+
expect(last_response.body).to eq({:errors => nil, :results => { :gotten => "success 2" }}.to_json)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe :authorization do
|
42
|
+
it "should try to authorize" do
|
43
|
+
get '/', {}, @headers
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe :status do
|
48
|
+
it "should return status code 200" do
|
49
|
+
get '/', {}, @headers
|
50
|
+
|
51
|
+
expect(last_response.status).to eq(200)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe :empty_response do
|
56
|
+
it "should return an empty string response" do
|
57
|
+
get '/empty', {}, @headers
|
58
|
+
|
59
|
+
expect(last_response.body).to eq({:errors => nil, :results => nil}.to_json)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe :no_content do
|
64
|
+
it "should return a no content response with empty body" do
|
65
|
+
get '/nocontent', {}, @headers
|
66
|
+
|
67
|
+
expect(last_response.status).to eq(204)
|
68
|
+
expect(last_response.body).to be_empty
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should return a created response with empty body" do
|
72
|
+
get '/created', {}, @headers
|
73
|
+
|
74
|
+
expect(last_response.status).to eq(201)
|
75
|
+
expect(last_response.body).to be_empty
|
76
|
+
end
|
77
|
+
|
78
|
+
it "shoud return a created response with body" do
|
79
|
+
get '/created_with_body', {}, @headers
|
80
|
+
|
81
|
+
expect(last_response.status).to eq(201)
|
82
|
+
expect(last_response.body).to eq({:errors => nil, :results => {:created => true}}.to_json)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should return a not modified response with empty body" do
|
86
|
+
get '/notmodified', {}, @headers
|
87
|
+
|
88
|
+
expect(last_response.status).to eq(304)
|
89
|
+
expect(last_response.body).to be_empty
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should delete and return a no content response with empty body" do
|
93
|
+
delete '/nocontent', {}, @headers
|
94
|
+
|
95
|
+
expect(last_response.status).to eq(204)
|
96
|
+
expect(last_response.body).to be_empty
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe :json do
|
101
|
+
before(:each) do
|
102
|
+
@posted_body = { :success => true }
|
103
|
+
|
104
|
+
@expected_hash = {:errors => nil, :results => { :posted => @posted_body }}
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should get json" do
|
108
|
+
@expected_hash = {:errors => nil, :results => { :gotten => "success" }}
|
109
|
+
|
110
|
+
get '/', {}, @headers
|
111
|
+
|
112
|
+
expect(last_response.body).to eq(@expected_hash.to_json)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should set the request body to json" do
|
116
|
+
@headers['CONTENT_TYPE'] = 'application/json'
|
117
|
+
|
118
|
+
post '/', @posted_body.to_json, @headers
|
119
|
+
|
120
|
+
expect(last_response.body).to eq(@expected_hash.to_json)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should post xml and return json" do
|
124
|
+
@headers['CONTENT_TYPE'] = 'text/xml'
|
125
|
+
post '/', @posted_body.to_xml(:root => :request), @headers
|
126
|
+
|
127
|
+
expect(last_response.body).to eq(@expected_hash.to_json)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should post empty json and return json" do
|
131
|
+
post '/', "", @headers
|
132
|
+
|
133
|
+
@expected_hash[:results][:posted] = {}
|
134
|
+
expect(last_response.body).to eq(@expected_hash.to_json)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should post json with json string as body" do
|
138
|
+
post '/', @posted_body.to_json, @headers
|
139
|
+
|
140
|
+
expect(last_response.body).to eq(@expected_hash.to_json)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe :xml do
|
145
|
+
before(:each) do
|
146
|
+
@posted_body = { :success => true }
|
147
|
+
|
148
|
+
@expected_hash = {:errors => nil, :results => { :posted => @posted_body }}
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should get xml" do
|
152
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
153
|
+
@expected_hash = { :errors => nil, :results => { :gotten => 'success'} }
|
154
|
+
|
155
|
+
get '/', {}, @headers
|
156
|
+
|
157
|
+
expect(last_response.body).to eq(@expected_hash.to_xml(:root => :response))
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should set the request body to xml" do
|
161
|
+
@headers['CONTENT_TYPE'] = 'text/xml'
|
162
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
163
|
+
post '/', @posted_body.to_xml(:root => :request), @headers
|
164
|
+
|
165
|
+
expect(last_response.body).to eq(@expected_hash.to_xml(:root => :response))
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should post json and return xml" do
|
169
|
+
@headers['CONTENT_TYPE'] = 'application/json'
|
170
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
171
|
+
post '/', @posted_body.to_json, @headers
|
172
|
+
|
173
|
+
expect(last_response.body).to eq(@expected_hash.to_xml(:root => :response))
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should post json with json string as body" do
|
177
|
+
@headers['CONTENT_TYPE'] = 'text/xml'
|
178
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
179
|
+
post '/', @posted_body.to_xml(:root => :request), @headers
|
180
|
+
|
181
|
+
expect(last_response.body).to eq(@expected_hash.to_xml(:root => :response))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rest::Authentication::AuthenticationBase do
|
4
|
+
describe :authorized? do
|
5
|
+
it "should always be true" do
|
6
|
+
target = Rest::Authentication::AuthenticationBase.new
|
7
|
+
|
8
|
+
expect(target.authorized?(nil, 'key')).to be_truthy
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rest::Authentication::BasicAuthentication do
|
4
|
+
describe :initialize do
|
5
|
+
it "should initialize with valid keys" do
|
6
|
+
expected_keys = ['key1', 'key2']
|
7
|
+
target = Rest::Authentication::BasicAuthentication.new(expected_keys)
|
8
|
+
|
9
|
+
expect(target.instance_variable_get(:@valid_keys)).to eq(expected_keys)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :authorized? do
|
14
|
+
it "should authorized if empty valid array" do
|
15
|
+
target = Rest::Authentication::BasicAuthentication.new([])
|
16
|
+
expect(target.authorized?(nil, 'key')).to be_truthy
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should authorize the given key" do
|
20
|
+
target = Rest::Authentication::BasicAuthentication.new(['key1', 'key2'])
|
21
|
+
expect(target.authorized?(nil, 'key1')).to be_truthy
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should authorize the given key" do
|
25
|
+
target = Rest::Authentication::BasicAuthentication.new(['key1', 'key2'])
|
26
|
+
expect(target.authorized?(nil,'key')).to_not be_truthy
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rest::Application do
|
4
|
+
before(:each) do
|
5
|
+
@headers = { 'HTTP_AUTHORIZATION' => 'key1' }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe :invalid_request do
|
9
|
+
it "should return the invalid param messages" do
|
10
|
+
expected_body = {
|
11
|
+
:errors => [
|
12
|
+
"Invalid Request Format",
|
13
|
+
"param1 is not valid: Must be a String",
|
14
|
+
"param2 is not valid: Must be an Integer"
|
15
|
+
],
|
16
|
+
:results => nil
|
17
|
+
}
|
18
|
+
|
19
|
+
get '/invalid_request', {}, @headers
|
20
|
+
|
21
|
+
expect(last_response.body).to eq(expected_body.to_json)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rest::Application do
|
4
|
+
before(:each) do
|
5
|
+
@expected_body = {
|
6
|
+
:errors => [],
|
7
|
+
:results => nil
|
8
|
+
}
|
9
|
+
|
10
|
+
@headers = { 'HTTP_AUTHORIZATION' => 'key1' }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :error_default_handler do
|
14
|
+
it "should create a error response based on the body" do
|
15
|
+
@expected_body[:errors] << "This is the default error handler"
|
16
|
+
|
17
|
+
get '/default_error_handeling', {}, @headers
|
18
|
+
|
19
|
+
expect(last_response.status).to eq(400)
|
20
|
+
expect(last_response.body).to eq(@expected_body.to_json)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should create a hashed error response based on the body" do
|
24
|
+
@expected_body[:errors] = { :error => "This is the hashed error handler" }
|
25
|
+
|
26
|
+
get '/hashed_error_handeling', {}, @headers
|
27
|
+
|
28
|
+
expect(last_response.status).to eq(400)
|
29
|
+
expect(last_response.body).to eq(@expected_body.to_json)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe :error_404 do
|
34
|
+
it "should provide endpoint not found error" do
|
35
|
+
@expected_body[:errors] << "Endpoint not found"
|
36
|
+
|
37
|
+
get '/not_found', {}, @headers
|
38
|
+
|
39
|
+
expect(last_response.status).to eq(404)
|
40
|
+
expect(last_response.body).to eq(@expected_body.to_json)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe :error_401 do
|
45
|
+
before (:each) do
|
46
|
+
@expected_body[:errors] << "Unauthorized Access"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should create an unauthorized error in json" do
|
50
|
+
get '/unauthorized', {}, @headers
|
51
|
+
|
52
|
+
expect(last_response.status).to eq(401)
|
53
|
+
expect(last_response.body).to eq(@expected_body.to_json)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "shoud create an unauthorized error in xml" do
|
57
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
58
|
+
get '/unauthorized', {}, @headers
|
59
|
+
|
60
|
+
expect(last_response.status).to eq(401)
|
61
|
+
expect(last_response.body).to eq(@expected_body.to_xml(:root => :response))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe :error_500 do
|
66
|
+
it "should create an array of errors with the error message in json when halted" do
|
67
|
+
@expected_body[:errors] << "Internal Error"
|
68
|
+
|
69
|
+
get '/internal_error', {}, @headers
|
70
|
+
|
71
|
+
expect(last_response.status).to eq(500)
|
72
|
+
expect(last_response.body).to eq(@expected_body.to_json)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should create an array of errors with the error message in xml when halted" do
|
76
|
+
@expected_body[:errors] << "Internal Error"
|
77
|
+
|
78
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
79
|
+
get '/internal_error', {}, @headers
|
80
|
+
|
81
|
+
expect(last_response.status).to eq(500)
|
82
|
+
expect(last_response.body).to eq(@expected_body.to_xml(:root => :response))
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should create an array of errors with the error message in json when raised" do
|
86
|
+
@expected_body[:errors] << "Raised Internal Error"
|
87
|
+
|
88
|
+
get '/internal_error_raised', {}, @headers
|
89
|
+
|
90
|
+
expect(last_response.status).to eq(500)
|
91
|
+
expect(last_response.body).to eq(@expected_body.to_json)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should create an array of errors with the error message in xml when raised" do
|
95
|
+
@expected_body[:errors] << "Raised Internal Error"
|
96
|
+
|
97
|
+
@headers['HTTP_ACCEPT'] = 'text/xml'
|
98
|
+
get '/internal_error_raised', {}, @headers
|
99
|
+
|
100
|
+
expect(last_response.status).to eq(500)
|
101
|
+
expect(last_response.body).to eq(@expected_body.to_xml(:root => :response))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'rack/test'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
SimpleCov.start do
|
6
|
+
add_filter '/spec/'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rest'
|
10
|
+
Dir[File.dirname(__FILE__) + '/fixtures/**/*.rb'].each {|file| require file }
|
11
|
+
|
12
|
+
ENV['RACK_ENV'] = 'test'
|
13
|
+
|
14
|
+
module RSpecMixin
|
15
|
+
include Rack::Test::Methods
|
16
|
+
def app() Rest::Test::TestApplication end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.configure do |conf|
|
20
|
+
conf.include RSpecMixin
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-rest-base
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- bmills
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thin
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: builder
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.2'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.6'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.6'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rack-test
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.6'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.6'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.8'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.8'
|
153
|
+
description: Base class for quickly setting up RESTful services through Sinatra.
|
154
|
+
email:
|
155
|
+
- brandon.mills@careerbuilder.com
|
156
|
+
executables: []
|
157
|
+
extensions: []
|
158
|
+
extra_rdoc_files: []
|
159
|
+
files:
|
160
|
+
- ".gitignore"
|
161
|
+
- Gemfile
|
162
|
+
- LICENSE.txt
|
163
|
+
- README.md
|
164
|
+
- Rakefile
|
165
|
+
- lib/rest.rb
|
166
|
+
- lib/rest/application.rb
|
167
|
+
- lib/rest/application_helper.rb
|
168
|
+
- lib/rest/authentication.rb
|
169
|
+
- lib/rest/authentication/authentication_base.rb
|
170
|
+
- lib/rest/authentication/basic_authentication.rb
|
171
|
+
- lib/rest/error_handeling.rb
|
172
|
+
- lib/rest/error_status_handeling.rb
|
173
|
+
- lib/rest/exceptions.rb
|
174
|
+
- lib/rest/exceptions/invalid_request.rb
|
175
|
+
- lib/rest/version.rb
|
176
|
+
- sinatra-rest-base.gemspec
|
177
|
+
- spec/fixtures/test_application.rb
|
178
|
+
- spec/fixtures/test_end_points.rb
|
179
|
+
- spec/fixtures/test_errors_end_points.rb
|
180
|
+
- spec/lib/rest/application_spec.rb
|
181
|
+
- spec/lib/rest/authentication/authentication_base_spec.rb
|
182
|
+
- spec/lib/rest/authentication/basic_authentication_spec.rb
|
183
|
+
- spec/lib/rest/error_handeling_spec.rb
|
184
|
+
- spec/lib/rest/error_status_handeling_spec.rb
|
185
|
+
- spec/spec_helper.rb
|
186
|
+
homepage: ''
|
187
|
+
licenses:
|
188
|
+
- MIT
|
189
|
+
metadata: {}
|
190
|
+
post_install_message:
|
191
|
+
rdoc_options: []
|
192
|
+
require_paths:
|
193
|
+
- lib
|
194
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - ">="
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
199
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">="
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: '0'
|
204
|
+
requirements: []
|
205
|
+
rubyforge_project:
|
206
|
+
rubygems_version: 2.4.4
|
207
|
+
signing_key:
|
208
|
+
specification_version: 4
|
209
|
+
summary: Base class for quickly setting up RESTful services through Sinatra.
|
210
|
+
test_files:
|
211
|
+
- spec/fixtures/test_application.rb
|
212
|
+
- spec/fixtures/test_end_points.rb
|
213
|
+
- spec/fixtures/test_errors_end_points.rb
|
214
|
+
- spec/lib/rest/application_spec.rb
|
215
|
+
- spec/lib/rest/authentication/authentication_base_spec.rb
|
216
|
+
- spec/lib/rest/authentication/basic_authentication_spec.rb
|
217
|
+
- spec/lib/rest/error_handeling_spec.rb
|
218
|
+
- spec/lib/rest/error_status_handeling_spec.rb
|
219
|
+
- spec/spec_helper.rb
|
220
|
+
has_rdoc:
|