roda-rest_api 1.4.5 → 2.0.1
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/.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
|