roda-rest_api 1.4.5 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.document +5 -0
- data/Gemfile +10 -0
- data/README.md +302 -0
- data/Rakefile +22 -0
- data/VERSION +1 -0
- data/lib/roda/plugins/rest_api.rb +302 -250
- data/roda-rest_api.gemspec +61 -0
- data/test/rest_api_api_test.rb +76 -0
- data/test/rest_api_form_input_test.rb +31 -0
- data/test/rest_api_id_pattern_test.rb +56 -0
- data/test/rest_api_nested_test.rb +105 -0
- data/test/rest_api_perf_benchmark.rb +35 -0
- data/test/rest_api_permit_test.rb +117 -0
- data/test/rest_api_resource_test.rb +127 -0
- data/test/rest_api_routes_test.rb +61 -0
- data/test/rest_api_serialize_test.rb +81 -0
- data/test/rest_api_singleton_test.rb +52 -0
- data/test/rest_api_split_route_test.rb +30 -0
- data/test/rest_api_upgrade_test.rb +22 -0
- data/test/rest_api_wrapper_test.rb +92 -0
- data/test/test_helpers.rb +93 -0
- metadata +27 -48
@@ -0,0 +1,127 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiResourceTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
app :rest_api do |r|
|
8
|
+
r.resource :albums do |rsc|
|
9
|
+
rsc.list { |params| Album.find(params) }
|
10
|
+
rsc.one { |params| Album[params[:id]] }
|
11
|
+
rsc.delete { |params| Album[params[:id]].destroy }
|
12
|
+
rsc.save { |atts| Album.create_or_update(atts) }
|
13
|
+
rsc.permit :name
|
14
|
+
end
|
15
|
+
r.resource :artists, primary_key: 'artist_id' do |rsc|
|
16
|
+
rsc.list { |params| Artist.find(params) }
|
17
|
+
rsc.one { |params| Artist[params[:artist_id]] }
|
18
|
+
rsc.routes :index, :show, :create
|
19
|
+
r.destroy do
|
20
|
+
'destroy artist'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_index
|
27
|
+
assert_equal Album.find({}).to_json, request.get('/albums').body
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_index_with_params
|
31
|
+
albums = Album.find({:page => 3})
|
32
|
+
assert_equal albums.to_json, request.get('/albums', {'QUERY_STRING' => 'page=3'}).body
|
33
|
+
assert_equal albums.to_json, request.get('/albums', params:{page:'3'}).body
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_show
|
37
|
+
assert_equal Album[12].to_json, request.get('/albums/12').body
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_notfound_id
|
41
|
+
assert_equal 404, request.get('/albums/13').status
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_create
|
45
|
+
id, name = 1, 'bar'
|
46
|
+
album = Album.new(id, name)
|
47
|
+
response = request.post('/albums', input: {name: name}.to_json)
|
48
|
+
assert_equal album.to_json, response.body
|
49
|
+
assert_equal 201, response.status
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_create_empty
|
53
|
+
response = request.post('/albums', input: "")
|
54
|
+
assert_equal 422, response.status
|
55
|
+
assert_match /at least contain two octets/, response.body
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_create_error
|
59
|
+
response = request.post('/albums', input: "illegal")
|
60
|
+
assert_match /unexpected token/, response.body
|
61
|
+
assert_equal 422, response.status
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_update_error
|
65
|
+
assert_equal 422, request.put('/albums/12', input: "illegal").status
|
66
|
+
assert_equal 422, request.patch('/albums/12', input: "illegal").status
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_update_album
|
70
|
+
id, name = 12, 'foo'
|
71
|
+
album = Album.new(id, name)
|
72
|
+
assert_equal album.to_json, request.patch('/albums/12', input: {id: id, name: name}.to_json).body
|
73
|
+
assert_equal album.to_json, request.put('/albums/12', input: {id: id, name: name}.to_json).body
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_destroy
|
77
|
+
response = request.delete('/albums/12')
|
78
|
+
assert_equal '', response.body
|
79
|
+
assert_equal 204, response.status
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_edit
|
83
|
+
assert_equal Album[12].to_json, request.get('/albums/12/edit').body
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_new
|
87
|
+
assert_equal Album.new.to_json, request.get('/albums/new').body
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_album_show_status
|
91
|
+
assert_equal 200, request.get('/albums/12').status
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_undefined_path
|
95
|
+
assert_equal 404, request.get('/albums/--').status
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_artist_index
|
99
|
+
assert_equal Artist.find({}).to_json, request.get('/artists').body
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_artist_show
|
103
|
+
assert_equal Artist[12].to_json, request.get('/artists/12').body
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_artist_show_status
|
107
|
+
assert_equal 200, request.get('/artists/12').status
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_artist_custom_destroy
|
111
|
+
assert_equal '', request.delete('/artists/12').body
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_artist_not_implemented
|
115
|
+
assert_raises(NotImplementedError) { request.post('/artists', input: {'name' => 'foo'}.to_json).body }
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_artist_method_not_defined
|
119
|
+
assert_equal 404, request.put('/artists/12', input: {'name' => 'foo'}.to_json).status
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_artist_not_found_path
|
123
|
+
assert_equal 404, request.get('/artistss').status
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiRoutesTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
app :rest_api do |r|
|
8
|
+
r.resource :albums do
|
9
|
+
r.index {"album index"}
|
10
|
+
r.new {"album new"}
|
11
|
+
r.show {|id| "album #{id} show"}
|
12
|
+
r.update {|id| "album #{id} update"}
|
13
|
+
r.destroy {|id| "album #{id} destroy"}
|
14
|
+
r.create {"album create"}
|
15
|
+
r.edit {|id| "album #{id} edit"}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_index_no_slash
|
21
|
+
assert_equal 'album index', request.get('/albums').body
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_index_slash
|
25
|
+
assert_equal 'album index', request.get('/albums/').body
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_show
|
29
|
+
assert_equal 'album 12 show', request.get('/albums/12').body
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_create_no_slash
|
33
|
+
assert_equal 'album create', request.post('/albums').body
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_create_slash
|
37
|
+
assert_equal 'album create', request.post('/albums/').body
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_update_patch
|
41
|
+
assert_equal 'album 12 update', request.patch('/albums/12').body
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_update_put
|
45
|
+
assert_equal 'album 12 update', request.put('/albums/12').body
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_edit
|
49
|
+
assert_equal 'album 12 edit', request.get('/albums/12/edit').body
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_new
|
53
|
+
assert_equal 'album new', request.get('/albums/new').body
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_fail
|
57
|
+
assert_equal 404, request.get('/albumss').status
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiSerializeTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
class TestSerializer
|
7
|
+
def serialize(res)
|
8
|
+
"{{#{res.id}}}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ContentTypeSerializer
|
13
|
+
def serialize(res)
|
14
|
+
"FOO#{res.id}BAR"
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'foo/bar'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup
|
23
|
+
app :rest_api do |r|
|
24
|
+
r.resource :albums, content_type: 'text/xml' do |rsc|
|
25
|
+
rsc.list { |params| Album.find(params) }
|
26
|
+
rsc.one { |params| Album[params[:id]] }
|
27
|
+
rsc.serialize { |result| result.is_a?(Enumerable) ? "<xml>#{result.map(&:id).join(',')}</xml>" : "<xml>#{result.id}</xml>" }
|
28
|
+
rsc.routes :index, :show
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_index
|
34
|
+
response = request.get('/albums')
|
35
|
+
assert_equal "<xml>1,2</xml>", response.body
|
36
|
+
assert_equal 'text/xml', response.headers['Content-Type']
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_show
|
40
|
+
response = request.get('/albums/12')
|
41
|
+
assert_equal "<xml>12</xml>", response.body
|
42
|
+
assert_equal 'text/xml', response.headers['Content-Type']
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_default_serialize
|
46
|
+
app :rest_api do |r|
|
47
|
+
r.resource :albums, serializer: TestSerializer.new do |rsc|
|
48
|
+
rsc.one { |atts| Album[atts[:id]] }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
assert_equal "{{12}}", request.get('/albums/12').body
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_content_type_serialize
|
55
|
+
app :rest_api do |r|
|
56
|
+
r.resource :albums, serializer: ContentTypeSerializer.new do |rsc|
|
57
|
+
rsc.one { |atts| Album[atts[:id]] }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
response = request.get('/albums/12')
|
61
|
+
assert_equal "FOO12BAR", response.body
|
62
|
+
assert_equal 'foo/bar', response.headers['Content-Type']
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def test_override_serialize
|
67
|
+
app :rest_api do |r|
|
68
|
+
r.api serializer: TestSerializer.new do
|
69
|
+
r.resource :albums do |rsc|
|
70
|
+
rsc.one { |atts| Album[atts[:id]] }
|
71
|
+
rsc.serialize {|res| "++#{res.id}++"}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
assert_equal "++12++", request.get('api/albums/12').body
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiSingletonTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
app :rest_api do |r|
|
8
|
+
r.resource :profile, singleton: true do |rsc|
|
9
|
+
rsc.one { |params| Profile[params[:id] || 12]}
|
10
|
+
rsc.save { |atts| Profile.create_or_update(atts) }
|
11
|
+
rsc.delete { |params| Profile[12].destroy }
|
12
|
+
rsc.permit :name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_singleton_show
|
18
|
+
assert_equal Profile[12].to_json, request.get('/profile').body
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_singleton_create
|
22
|
+
name = 'bar'
|
23
|
+
album = Profile.new(1, name)
|
24
|
+
assert_equal album.to_json, request.post('/profile', input: {name: name}.to_json).body
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_singleton_update
|
28
|
+
name = 'bar'
|
29
|
+
album = Profile.new(1, name)
|
30
|
+
assert_equal album.to_json, request.patch('/profile', input: {name: name}.to_json).body
|
31
|
+
assert_equal album.to_json, request.put('/profile', input: {name: name}.to_json).body
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_singleton_destroy
|
35
|
+
response = request.delete('/profile')
|
36
|
+
assert_equal '', response.body
|
37
|
+
assert_equal 204, response.status
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_singleton_edit
|
41
|
+
assert_equal Profile[12].to_json, request.get('/profile/edit').body
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_singleton_new
|
45
|
+
assert_equal Profile.new.to_json, request.get('/profile/new').body
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
class Profile < Mock; end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiSplitRoutesTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
def setup
|
9
|
+
app :rest_api do |r|
|
10
|
+
def anything
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
r.resource :things do |u|
|
15
|
+
u.one { |params| "one" }
|
16
|
+
u.list { |params| "list" }
|
17
|
+
u.routes :show
|
18
|
+
u.routes :index do
|
19
|
+
anything
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_split_routes
|
26
|
+
assert_equal "one", request.get("/things/1").body
|
27
|
+
assert_equal "list", request.get("/things").body
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiUpgradeTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def test_raise_serialize
|
7
|
+
assert_raises RuntimeError do
|
8
|
+
app(:rest_api, serialize: 1)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_raise_content_type
|
13
|
+
assert_raises RuntimeError do
|
14
|
+
app(:rest_api, content_type: 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_not_raise_other
|
19
|
+
assert app(:rest_api, other: 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiWrapperTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
|
7
|
+
module Wrapper
|
8
|
+
def around_save(args)
|
9
|
+
if opts[:foo]
|
10
|
+
"#{opts[:foo]}-#{yield(args)}-#{opts[:foo]}"
|
11
|
+
else
|
12
|
+
"WRAP-#{yield(args)}-WRAP"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module FailingWrapper
|
19
|
+
def around_save(args)
|
20
|
+
raise "Not Allowed"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ModifyingWrapper
|
25
|
+
def around_save(args)
|
26
|
+
args[:foo] = 'baz'
|
27
|
+
yield(args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def test_default_wrapper
|
33
|
+
app :rest_api do |r|
|
34
|
+
r.api wrapper: Wrapper do
|
35
|
+
r.resource :albums do |rsc|
|
36
|
+
rsc.save { |atts| 'ALBUM' }
|
37
|
+
rsc.list { |atts| 'LIST' }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
assert_equal "WRAP-ALBUM-WRAP", request.post('api/albums', params:{foo:'bar'}).body
|
42
|
+
assert_equal 'LIST', request.get('api/albums').body
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def test_custom_option_wrapper
|
47
|
+
app :rest_api do |r|
|
48
|
+
r.api wrapper: Wrapper do
|
49
|
+
r.resource :albums, resource: {foo:'BARZ'} do |rsc|
|
50
|
+
rsc.save { |atts| 'ALBUM' }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
assert_equal "BARZ-ALBUM-BARZ", request.post('api/albums', params:{foo:'bar'}).body
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_failing_wrapper
|
58
|
+
app :rest_api do |r|
|
59
|
+
r.resource :albums, wrapper: FailingWrapper do |rsc|
|
60
|
+
rsc.save { |atts| 'ALBUM' }
|
61
|
+
rsc.list { |atts| 'LIST' }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
assert_equal 422, request.post('/albums', params:{foo:'bar'}).status
|
65
|
+
assert_equal 'LIST', request.get('/albums').body
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_faulty_wrapper
|
69
|
+
app(:rest_api) do |r|
|
70
|
+
r.resource :albums, wrapper: :invalid do |rsc|
|
71
|
+
rsc.list { |atts| 'LIST' }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
assert_raises RuntimeError do
|
75
|
+
request.get('/albums').body
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def test_modifying_wrapper
|
81
|
+
app :rest_api do |r|
|
82
|
+
r.version 3, wrapper: ModifyingWrapper do
|
83
|
+
r.resource :albums do |rsc|
|
84
|
+
rsc.save { |atts| atts[:foo] }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
assert_equal 'baz', request.post('v3/albums', params:{foo:'bar'}).body
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|