hyperdrive 0.0.5 → 0.0.6
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 +4 -4
- data/.travis.yml +12 -4
- data/Gemfile +2 -0
- data/hyperdrive.gemspec +6 -2
- data/lib/hyperdrive/docs.rb +10 -11
- data/lib/hyperdrive/dsl/resource.rb +10 -15
- data/lib/hyperdrive/dsl.rb +49 -3
- data/lib/hyperdrive/endpoint.rb +65 -0
- data/lib/hyperdrive/errors/missing_required_param.rb +19 -0
- data/lib/hyperdrive/errors/not_acceptable.rb +18 -0
- data/lib/hyperdrive/errors.rb +13 -8
- data/lib/hyperdrive/filter.rb +20 -0
- data/lib/hyperdrive/hateoas.rb +45 -0
- data/lib/hyperdrive/middleware/accept.rb +16 -0
- data/lib/hyperdrive/middleware/content_negotiation.rb +19 -0
- data/lib/hyperdrive/middleware/cors.rb +36 -0
- data/lib/hyperdrive/middleware/error.rb +35 -0
- data/lib/hyperdrive/middleware/request_method.rb +31 -0
- data/lib/hyperdrive/middleware/required_params.rb +35 -0
- data/lib/hyperdrive/middleware/resource.rb +18 -0
- data/lib/hyperdrive/middleware/sanitize_params.rb +25 -0
- data/lib/hyperdrive/middleware.rb +10 -0
- data/lib/hyperdrive/param.rb +44 -0
- data/lib/hyperdrive/resource.rb +74 -30
- data/lib/hyperdrive/server.rb +16 -17
- data/lib/hyperdrive/utils.rb +27 -0
- data/lib/hyperdrive/values.rb +37 -3
- data/lib/hyperdrive/version.rb +1 -1
- data/lib/hyperdrive.rb +21 -7
- data/spec/hyperdrive/docs_spec.rb +14 -8
- data/spec/hyperdrive/dsl/resource_spec.rb +27 -37
- data/spec/hyperdrive/dsl_spec.rb +52 -0
- data/spec/hyperdrive/endpoint_spec.rb +22 -0
- data/spec/hyperdrive/filter_spec.rb +34 -0
- data/spec/hyperdrive/hateoas_spec.rb +42 -0
- data/spec/hyperdrive/middleware/accept_spec.rb +15 -0
- data/spec/hyperdrive/middleware/content_negotiation_spec.rb +29 -0
- data/spec/hyperdrive/middleware/cors_spec.rb +47 -0
- data/spec/hyperdrive/middleware/error_spec.rb +25 -0
- data/spec/hyperdrive/middleware/request_method_spec.rb +28 -0
- data/spec/hyperdrive/middleware/required_params_spec.rb +43 -0
- data/spec/hyperdrive/middleware/resource_spec.rb +15 -0
- data/spec/hyperdrive/middleware/sanitize_params_spec.rb +24 -0
- data/spec/hyperdrive/param_spec.rb +34 -0
- data/spec/hyperdrive/resource_spec.rb +87 -25
- data/spec/hyperdrive/server_spec.rb +48 -7
- data/spec/hyperdrive/utils_spec.rb +38 -0
- data/spec/hyperdrive/values_spec.rb +37 -0
- data/spec/spec_helper.rb +71 -12
- metadata +106 -11
- data/.coveralls.yml +0 -1
- data/lib/hyperdrive/dsl/main.rb +0 -19
- data/lib/hyperdrive/response.rb +0 -42
- data/spec/hyperdrive/dsl/main_spec.rb +0 -16
- data/spec/hyperdrive/response_spec.rb +0 -23
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hyperdrive
|
4
|
+
class Param
|
5
|
+
attr_reader :name, :description, :required, :type, :constraints
|
6
|
+
|
7
|
+
def initialize(name, description, options = {})
|
8
|
+
@name = name.to_s
|
9
|
+
@description = description
|
10
|
+
options = default_options.merge(options)
|
11
|
+
@required = if options[:required] == true
|
12
|
+
%w(POST PUT PATCH)
|
13
|
+
elsif options[:required] == false
|
14
|
+
[]
|
15
|
+
else
|
16
|
+
Array(options[:required])
|
17
|
+
end
|
18
|
+
@type = options[:type]
|
19
|
+
@constraints = "#{required_constraint} #{options[:constraints]}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def required?(http_request_method)
|
23
|
+
@required.include? http_request_method
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
{ name: @name, description: @description, type: @type, constraints: @constraints }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def default_options
|
33
|
+
{ required: true, type: 'String', constraints: nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
def required_constraint
|
37
|
+
if @required.empty?
|
38
|
+
''
|
39
|
+
else
|
40
|
+
"Required for: #{@required.join(', ')}."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/hyperdrive/resource.rb
CHANGED
@@ -2,37 +2,62 @@
|
|
2
2
|
|
3
3
|
module Hyperdrive
|
4
4
|
class Resource
|
5
|
-
|
6
|
-
|
5
|
+
include Hyperdrive::Values
|
6
|
+
attr_reader :id, :namespace, :endpoint, :params, :filters, :request_handlers, :version
|
7
|
+
attr_accessor :name, :description
|
7
8
|
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@endpoint = "/#{
|
11
|
-
@
|
9
|
+
def initialize(name, hyperdrive_config = hyperdrive.config)
|
10
|
+
@namespace = name.to_s.en.plural
|
11
|
+
@endpoint = "/#{namespace}"
|
12
|
+
@params = default_params
|
12
13
|
@filters = default_filters
|
13
14
|
@request_handlers = default_request_handlers
|
15
|
+
@config = hyperdrive_config
|
16
|
+
@id = [@config[:vendor], @namespace].join(':')
|
14
17
|
end
|
15
18
|
|
16
|
-
def register_param(
|
17
|
-
|
18
|
-
@allowed_params[key] = { desc: description }.merge(options)
|
19
|
+
def register_param(param, description, options = {})
|
20
|
+
@params[param] = Param.new(param, description, options)
|
19
21
|
end
|
20
22
|
|
21
|
-
def register_filter(
|
22
|
-
|
23
|
-
@filters[key] = { desc: description }.merge(options)
|
23
|
+
def register_filter(filter, description, options = {})
|
24
|
+
@filters[filter] = Filter.new(filter, description, options)
|
24
25
|
end
|
25
26
|
|
26
|
-
def
|
27
|
-
@request_handlers[request_method]
|
27
|
+
def register_request_handler(request_method, request_handler, version = 'v1')
|
28
|
+
@request_handlers[request_method] ||= {}
|
29
|
+
@request_handlers[request_method].merge!({ version => request_handler })
|
28
30
|
if request_method == :get
|
29
|
-
@request_handlers[:head]
|
31
|
+
@request_handlers[:head] ||= {}
|
32
|
+
@request_handlers[:head].merge!({ version => @request_handlers[:get][version] })
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
def request_handler(http_request_method)
|
34
|
-
|
35
|
-
|
36
|
+
def request_handler(http_request_method, version = nil)
|
37
|
+
version ||= latest_version(http_request_method)
|
38
|
+
request_method = http_request_methods[http_request_method]
|
39
|
+
request_handlers[request_method][version]
|
40
|
+
end
|
41
|
+
|
42
|
+
def acceptable_content_types(http_request_method)
|
43
|
+
content_types = []
|
44
|
+
@config[:media_types].each do |media_type|
|
45
|
+
available_versions(http_request_method).each do |version|
|
46
|
+
content_types << "application/vnd.#{@config[:vendor]}.#{namespace}.#{version}+#{media_type}"
|
47
|
+
end
|
48
|
+
content_types << "application/vnd.#{@config[:vendor]}.#{namespace}+#{media_type}"
|
49
|
+
content_types << "application/vnd.#{@config[:vendor]}+#{media_type}"
|
50
|
+
end
|
51
|
+
content_types
|
52
|
+
end
|
53
|
+
|
54
|
+
def available_versions(http_request_method)
|
55
|
+
request_method = http_request_methods[http_request_method]
|
56
|
+
@request_handlers[request_method].keys.sort.reverse
|
57
|
+
end
|
58
|
+
|
59
|
+
def latest_version(http_request_method)
|
60
|
+
available_versions(http_request_method).first
|
36
61
|
end
|
37
62
|
|
38
63
|
def request_method_allowed?(http_request_method)
|
@@ -40,33 +65,52 @@ module Hyperdrive
|
|
40
65
|
end
|
41
66
|
|
42
67
|
def allowed_methods
|
43
|
-
|
68
|
+
request_methods.values_at(*request_handlers.keys)
|
44
69
|
end
|
45
70
|
|
46
|
-
|
71
|
+
def required_param?(param, http_request_method)
|
72
|
+
return false if %w(GET HEAD OPTIONS).include? http_request_method
|
73
|
+
params.key?(param) and params[param].required?(http_request_method)
|
74
|
+
end
|
47
75
|
|
48
|
-
def
|
76
|
+
def required_filter?(filter, http_request_method)
|
77
|
+
return false unless %w(GET HEAD OPTIONS).include? http_request_method
|
78
|
+
filters.key?(filter) and filters[filter].required?(http_request_method)
|
79
|
+
end
|
80
|
+
|
81
|
+
def required?(param, http_request_method)
|
82
|
+
required_param?(param, http_request_method) or required_filter?(param, http_request_method)
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_hash
|
49
86
|
{
|
50
|
-
|
87
|
+
_links: { 'self' => { href: endpoint } },
|
88
|
+
id: id,
|
89
|
+
name: name,
|
90
|
+
description: description,
|
91
|
+
methods: allowed_methods,
|
92
|
+
params: params.map { |_,param| param.to_hash },
|
93
|
+
filters: filters.map { |_,filter| filter.to_hash },
|
94
|
+
media_types: allowed_methods.map { |method| acceptable_content_types(method) }.uniq
|
51
95
|
}
|
52
96
|
end
|
53
97
|
|
54
|
-
|
55
|
-
{ required: true }.freeze
|
56
|
-
end
|
98
|
+
private
|
57
99
|
|
58
|
-
def
|
100
|
+
def default_params
|
59
101
|
{
|
60
|
-
id:
|
102
|
+
id: Param.new(:id, 'Identifier', required: %w(PUT PATCH DELETE))
|
61
103
|
}
|
62
104
|
end
|
63
105
|
|
64
|
-
def
|
65
|
-
{
|
106
|
+
def default_filters
|
107
|
+
{
|
108
|
+
id: Filter.new(:id, 'Resource Identifier', required: false)
|
109
|
+
}
|
66
110
|
end
|
67
111
|
|
68
112
|
def default_request_handlers
|
69
|
-
{ options: proc { '' } }
|
113
|
+
{ options: { 'v1' => proc { |env| '' } } }
|
70
114
|
end
|
71
115
|
end
|
72
116
|
end
|
data/lib/hyperdrive/server.rb
CHANGED
@@ -10,30 +10,29 @@ module Hyperdrive
|
|
10
10
|
|
11
11
|
def self.server
|
12
12
|
Rack::Builder.new do
|
13
|
-
use Rack::Runtime
|
14
13
|
use Rack::Lint
|
14
|
+
use Rack::Runtime
|
15
|
+
use Rack::MethodOverride
|
15
16
|
use Rack::Head
|
17
|
+
use Rack::ConditionalGet
|
18
|
+
use Hyperdrive::Middleware::Error
|
19
|
+
use Hyperdrive::Middleware::Accept
|
20
|
+
use Rack::Deflater
|
21
|
+
use Rack::ETag, "max-age=0,private,must-revalidate", "public,max-age=86400,s-maxage=86400"
|
16
22
|
|
17
23
|
map '/' do
|
18
|
-
|
19
|
-
hyperdrive.resources.each do |type, resource|
|
20
|
-
info += %Q({"id":"#{resource.endpoint}","name":"#{resource.name}","desc":"#{resource.desc}","type":"#{type}"})
|
21
|
-
end
|
22
|
-
|
23
|
-
run ->(env) {
|
24
|
-
[200, { 'Content-Type' => 'application/json', 'Allow' => Hyperdrive::Values.request_methods.join(",") }, ["[#{info}]"]]
|
25
|
-
}
|
24
|
+
run Hyperdrive::HATEOAS
|
26
25
|
end
|
27
26
|
|
28
|
-
hyperdrive.resources.each do |key, resource|
|
27
|
+
hyperdrive.resources.each do |key, resource|
|
29
28
|
map resource.endpoint do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
use Hyperdrive::Middleware::Resource, resource
|
30
|
+
use Hyperdrive::Middleware::RequestMethod
|
31
|
+
use Hyperdrive::Middleware::SanitizeParams
|
32
|
+
use Hyperdrive::Middleware::RequiredParams
|
33
|
+
use Hyperdrive::Middleware::CORS, hyperdrive.config[:cors]
|
34
|
+
use Hyperdrive::Middleware::ContentNegotiation
|
35
|
+
run Hyperdrive::Endpoint
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end.to_app
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Hyperdrive
|
4
|
+
module Utils
|
5
|
+
def self.sanitize_keys(keys_to_keep, hash)
|
6
|
+
Hash[hash.select do |key, value|
|
7
|
+
keys_to_keep.include? key
|
8
|
+
end]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.symbolize_keys(hash)
|
12
|
+
hash.inject({}) do |result, (key, value)|
|
13
|
+
result.merge!(Hash[
|
14
|
+
case key
|
15
|
+
when String then key.to_sym
|
16
|
+
else key
|
17
|
+
end,
|
18
|
+
case value
|
19
|
+
when Hash then symbolize_keys(value)
|
20
|
+
when Array then value.map! { |v| v.is_a?(Hash) ? symbolize_keys(v) : v } ; value
|
21
|
+
else value
|
22
|
+
end
|
23
|
+
])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/hyperdrive/values.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Hyperdrive
|
2
4
|
module Values
|
3
|
-
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def definable_request_methods
|
8
|
+
[:get, :post, :put, :patch, :delete].freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def supported_request_methods
|
4
12
|
%w(GET HEAD OPTIONS POST PUT PATCH DELETE).freeze
|
5
13
|
end
|
6
14
|
|
7
|
-
def
|
15
|
+
def request_methods
|
8
16
|
{
|
9
17
|
get: 'GET',
|
10
18
|
head: 'HEAD',
|
@@ -16,7 +24,7 @@ module Hyperdrive
|
|
16
24
|
}.freeze
|
17
25
|
end
|
18
26
|
|
19
|
-
def
|
27
|
+
def http_request_methods
|
20
28
|
{
|
21
29
|
'GET' => :get,
|
22
30
|
'HEAD' => :head,
|
@@ -27,5 +35,31 @@ module Hyperdrive
|
|
27
35
|
'DELETE' => :delete
|
28
36
|
}.freeze
|
29
37
|
end
|
38
|
+
|
39
|
+
def default_cors_options
|
40
|
+
{
|
41
|
+
origins: '*',
|
42
|
+
allow_headers: 'Content-Type, Accept, Authorization, If-None-Match',
|
43
|
+
credentials: 'false',
|
44
|
+
expose_headers: 'Allow, Cache-Control, Content-Language, Content-Type, ETag',
|
45
|
+
max_age: 86400
|
46
|
+
}.freeze
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_config
|
50
|
+
{
|
51
|
+
cors: default_cors_options,
|
52
|
+
name: 'Hyperdrive API',
|
53
|
+
description: "v#{Hyperdrive::VERSION}",
|
54
|
+
vendor: 'hyperdrive',
|
55
|
+
media_types: %w(hal+json json)
|
56
|
+
}.freeze
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_headers
|
60
|
+
{
|
61
|
+
'X-Powered-By' => "Hyperdrive (v#{Hyperdrive::VERSION})"
|
62
|
+
}
|
63
|
+
end
|
30
64
|
end
|
31
65
|
end
|
data/lib/hyperdrive/version.rb
CHANGED
data/lib/hyperdrive.rb
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
#
|
3
|
+
# prepare for hyperspace!
|
4
|
+
|
5
|
+
## stdlib
|
4
6
|
require 'rack'
|
5
|
-
require '
|
6
|
-
Linguistics.use(:en)
|
7
|
+
require 'rack/accept'
|
8
|
+
require 'linguistics'; Linguistics.use(:en)
|
9
|
+
require 'oj'
|
7
10
|
|
8
|
-
|
11
|
+
## immutable values
|
12
|
+
require 'hyperdrive/values'
|
13
|
+
require 'hyperdrive/version'
|
14
|
+
|
15
|
+
## helpers
|
9
16
|
require 'hyperdrive/docs'
|
17
|
+
require 'hyperdrive/utils'
|
18
|
+
|
19
|
+
## sugary syntax and state mangagement
|
10
20
|
require 'hyperdrive/dsl'
|
11
21
|
require 'hyperdrive/errors'
|
22
|
+
require 'hyperdrive/param'
|
23
|
+
require 'hyperdrive/filter' # must come after param
|
12
24
|
require 'hyperdrive/resource'
|
13
|
-
|
25
|
+
|
26
|
+
## rack apps and middleware
|
27
|
+
require 'hyperdrive/endpoint'
|
28
|
+
require 'hyperdrive/hateoas'
|
29
|
+
require 'hyperdrive/middleware'
|
14
30
|
require 'hyperdrive/server'
|
15
|
-
require 'hyperdrive/values'
|
16
|
-
require 'hyperdrive/version'
|
@@ -4,17 +4,20 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Hyperdrive::Docs do
|
6
6
|
before do
|
7
|
-
|
8
|
-
@docs = Hyperdrive::Docs.new(
|
7
|
+
@resources = { :thing => default_resource }
|
8
|
+
@docs = Hyperdrive::Docs.new(@resources)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'generates a header with size 1 as default' do
|
12
12
|
@docs.header('Thing Resource').must_equal "\n\n# Thing Resource\n\n"
|
13
13
|
end
|
14
14
|
|
15
|
-
it '
|
16
|
-
proc {@docs.header('Thing Resource', 0)}.must_raise ArgumentError
|
17
|
-
|
15
|
+
it 'raises an error if header size is less than 1' do
|
16
|
+
proc { @docs.header('Thing Resource', 0) }.must_raise ArgumentError
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'raises an error if header size is greater than 6' do
|
20
|
+
proc { @docs.header('Thing Resource', 8) }.must_raise ArgumentError
|
18
21
|
end
|
19
22
|
|
20
23
|
it 'generates a paragraph' do
|
@@ -33,11 +36,14 @@ describe Hyperdrive::Docs do
|
|
33
36
|
@docs.bullet('test').must_equal " - test\n"
|
34
37
|
end
|
35
38
|
|
36
|
-
it '
|
37
|
-
proc {@docs.bullet('test', 4)}.must_raise ArgumentError
|
39
|
+
it 'raises an error if bullet indention size is less than 1' do
|
38
40
|
proc {@docs.bullet('test', 0)}.must_raise ArgumentError
|
39
41
|
end
|
40
42
|
|
43
|
+
it 'raises an error if bullet indention size is greater than 3' do
|
44
|
+
proc {@docs.bullet('test', 4)}.must_raise ArgumentError
|
45
|
+
end
|
46
|
+
|
41
47
|
it 'generates a nested bulleted list' do
|
42
48
|
@docs.bullet('test', 2).must_equal " - test\n"
|
43
49
|
end
|
@@ -53,4 +59,4 @@ describe Hyperdrive::Docs do
|
|
53
59
|
it 'outputs a string of the completed doc' do
|
54
60
|
@docs.output.must_be_kind_of String
|
55
61
|
end
|
56
|
-
end
|
62
|
+
end
|
@@ -3,56 +3,46 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Hyperdrive::DSL::Resource do
|
6
|
-
|
7
|
-
it "sets the name of the endpoint" do
|
6
|
+
before do
|
8
7
|
hyperdrive do
|
9
8
|
resource(:thing) do
|
10
|
-
name
|
9
|
+
name 'Thing Resource'
|
10
|
+
description 'Thing Description'
|
11
|
+
param :name, 'Thing Name'
|
12
|
+
filter :parent_id, "Parent ID"
|
13
|
+
request(:get) do
|
14
|
+
'ok'
|
15
|
+
end
|
11
16
|
end
|
12
|
-
end
|
17
|
+
end
|
13
18
|
end
|
14
19
|
|
15
|
-
|
16
|
-
hyperdrive
|
17
|
-
resource(:thing) do
|
18
|
-
desc "Thing Description"
|
19
|
-
end
|
20
|
-
end.resources[:thing].desc.must_equal 'Thing Description'
|
20
|
+
after do
|
21
|
+
hyperdrive.send(:reset!)
|
21
22
|
end
|
22
23
|
|
23
|
-
it "
|
24
|
-
hyperdrive
|
25
|
-
resource(:thing) do
|
26
|
-
param :name, "Thing's Name"
|
27
|
-
end
|
28
|
-
end.resources[:thing].allowed_params[:name][:desc].must_equal "Thing's Name"
|
24
|
+
it "sets the name of the resource" do
|
25
|
+
hyperdrive.resources[:thing].name.must_equal 'Thing Resource'
|
29
26
|
end
|
30
27
|
|
31
|
-
it "
|
32
|
-
hyperdrive
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
it "sets the description of the resource" do
|
29
|
+
hyperdrive.resources[:thing].description.must_equal 'Thing Description'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "registers a param for the resource" do
|
33
|
+
hyperdrive.resources[:thing].params[:name].description.must_equal "Thing Name"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "registers a filter for the resource" do
|
37
|
+
hyperdrive.resources[:thing].filters[:parent_id].description.must_equal "Parent ID"
|
37
38
|
end
|
38
39
|
|
39
40
|
it "defines how requests are handled" do
|
40
|
-
hyperdrive
|
41
|
-
resource(:thing) do
|
42
|
-
request(:get) do
|
43
|
-
'ok'
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end.resources[:thing].request_handlers[:get].call.must_equal 'ok'
|
41
|
+
hyperdrive.resources[:thing].request_handlers[:get]['v1'].must_be :===, Proc
|
47
42
|
end
|
48
43
|
|
49
|
-
it "
|
50
|
-
|
51
|
-
|
52
|
-
resource(:thing) do
|
53
|
-
request(:verb)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end.must_raise Hyperdrive::Errors::DSL::UnknownArgument
|
44
|
+
it "throws an error if request method is unknown" do
|
45
|
+
bad_resource = -> { hyperdrive { resource(:thing) { request(:verb) } } }
|
46
|
+
bad_resource.must_raise Hyperdrive::Errors::DSL::UnknownArgument
|
57
47
|
end
|
58
48
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Hyperdrive::DSL do
|
6
|
+
before do
|
7
|
+
hyperdrive do
|
8
|
+
name 'Example'
|
9
|
+
description 'Example Description'
|
10
|
+
vendor 'example'
|
11
|
+
media_types %w(json)
|
12
|
+
cors({ origins: '*', allow_headers: %w(Accept), test: 'test'})
|
13
|
+
resource(:thing) {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
hyperdrive.send(:reset!)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has a name" do
|
22
|
+
hyperdrive.config[:name].must_equal 'Example'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has a description" do
|
26
|
+
hyperdrive.config[:description].must_equal 'Example Description'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has a vendor" do
|
30
|
+
hyperdrive.config[:vendor].must_equal 'example'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "has media types" do
|
34
|
+
hyperdrive.config[:media_types].must_equal ['json']
|
35
|
+
end
|
36
|
+
|
37
|
+
it "registers a resource" do
|
38
|
+
hyperdrive.resources[:thing].must_be_instance_of ::Hyperdrive::Resource
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can configure cors options" do
|
42
|
+
hyperdrive.config[:cors][:allow_headers].must_equal ['Accept']
|
43
|
+
end
|
44
|
+
|
45
|
+
it "ensures missing options have default values" do
|
46
|
+
hyperdrive.config[:cors][:credentials].must_equal 'false'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "removes unsupported cors options" do
|
50
|
+
hyperdrive.config[:cors].key?(:test).must_equal false
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Hyperdrive::Endpoint do
|
6
|
+
def app
|
7
|
+
Hyperdrive::Endpoint
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
sample_api
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
hyperdrive.send(:reset!)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "responds to requests" do
|
19
|
+
get '/', {}, default_rack_env(hyperdrive.resources[:thing])
|
20
|
+
last_response.successful?.must_equal true
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Hyperdrive::Filter do
|
6
|
+
before do
|
7
|
+
@filter = default_filter
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has a name" do
|
11
|
+
@filter.name.must_equal 'parent_id'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a description" do
|
15
|
+
@filter.description.must_equal 'Parent Identifier'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns an array of HTTP methods it's required for" do
|
19
|
+
@filter.required.must_equal %w(GET HEAD)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns true if the param is required for the given HTTP method" do
|
23
|
+
@filter.required?('GET').must_equal true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns false if the param is not required for the given HTTP method" do
|
27
|
+
@filter.required?('OPTIONS').must_equal false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "converts itself as a hash" do
|
31
|
+
constraints = { name: 'parent_id', description: 'Parent Identifier', type: 'String', constraints: 'Required for: GET, HEAD. Must be a valid BSON Object ID.' }
|
32
|
+
@filter.to_hash.must_equal constraints
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Hyperdrive::HATEOAS do
|
6
|
+
def app
|
7
|
+
Hyperdrive::HATEOAS
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'without Resources' do
|
11
|
+
it "throws a Not Found error" do
|
12
|
+
->{ get '/' }.must_raise Hyperdrive::Errors::NotFound
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with Resources' do
|
17
|
+
before do
|
18
|
+
sample_api
|
19
|
+
get '/', {}, default_rack_env
|
20
|
+
end
|
21
|
+
|
22
|
+
after do
|
23
|
+
hyperdrive.send(:reset!)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "responds successfully" do
|
27
|
+
last_response.successful?.must_equal true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "sets the content type" do
|
31
|
+
last_response.headers['Content-Type'].must_equal 'application/json'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sets the allow header" do
|
35
|
+
last_response['Allow'].must_equal 'GET, HEAD, OPTIONS'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "responds with a description of all resources" do
|
39
|
+
last_response.body.must_equal %Q({"_links":{"self":{"href":"/"}},"name":"Hyperdrive API","description":"v0.0.5","vendor":"hyperdrive","resources":[{"_links":{"self":{"href":"/things"}},"id":"hyperdrive:things","name":"Thing Resource","description":"Description of Thing Resource","methods":["OPTIONS","GET","HEAD","POST","PUT","PATCH","DELETE"],"params":[{"name":"id","description":"Identifier","type":"String","constraints":"Required for: PUT, PATCH, DELETE. "},{"name":"name","description":"50 Chars or less","type":"String","constraints":"Required for: POST, PUT, PATCH. "}],"filters":[{"name":"id","description":"Resource Identifier","type":"String","constraints":" "},{"name":"parent_id","description":"Parent ID of Thing","type":"String","constraints":"Required for: GET, HEAD. "}],"media_types":[["application/vnd.hyperdrive.things.v1+hal+json","application/vnd.hyperdrive.things+hal+json","application/vnd.hyperdrive+hal+json","application/vnd.hyperdrive.things.v1+json","application/vnd.hyperdrive.things+json","application/vnd.hyperdrive+json"]]}]})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Hyperdrive::Middleware::Accept do
|
6
|
+
def app
|
7
|
+
inner_app = ->(env) { [200, {}, env['hyperdrive.accept'].values.join(", ")] }
|
8
|
+
Hyperdrive::Middleware::Accept.new(inner_app)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "parses the accept header" do
|
12
|
+
get '/', {}, default_rack_env
|
13
|
+
last_response.body.must_equal 'application/vnd.hyperdrive.things+hal+json, application/json'
|
14
|
+
end
|
15
|
+
end
|