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,61 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: roda-rest_api 2.0.1 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "roda-rest_api"
|
9
|
+
s.version = "2.0.1"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.authors = ["Michel Benevento"]
|
14
|
+
s.date = "2015-06-29"
|
15
|
+
s.description = "Create restful API easily with the Roda framework"
|
16
|
+
s.email = "michelbenevento@yahoo.com"
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
"Gemfile",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/roda/plugins/rest_api.rb",
|
27
|
+
"lib/roda/rest_api.rb",
|
28
|
+
"roda-rest_api.gemspec",
|
29
|
+
"test/rest_api_api_test.rb",
|
30
|
+
"test/rest_api_form_input_test.rb",
|
31
|
+
"test/rest_api_id_pattern_test.rb",
|
32
|
+
"test/rest_api_nested_test.rb",
|
33
|
+
"test/rest_api_perf_benchmark.rb",
|
34
|
+
"test/rest_api_permit_test.rb",
|
35
|
+
"test/rest_api_resource_test.rb",
|
36
|
+
"test/rest_api_routes_test.rb",
|
37
|
+
"test/rest_api_serialize_test.rb",
|
38
|
+
"test/rest_api_singleton_test.rb",
|
39
|
+
"test/rest_api_split_route_test.rb",
|
40
|
+
"test/rest_api_upgrade_test.rb",
|
41
|
+
"test/rest_api_wrapper_test.rb",
|
42
|
+
"test/test_helpers.rb"
|
43
|
+
]
|
44
|
+
s.homepage = "http://github.com/beno/roda-rest_api"
|
45
|
+
s.licenses = ["MIT"]
|
46
|
+
s.rubygems_version = "2.4.5"
|
47
|
+
s.summary = "Restful resources for Roda"
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 4
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<roda>, ["~> 2"])
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<roda>, ["~> 2"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<roda>, ["~> 2"])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiApiTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_api_version
|
10
|
+
app :rest_api do |r|
|
11
|
+
r.api do
|
12
|
+
r.version 1 do
|
13
|
+
'api version 1'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
assert_equal 'api version 1', request.get('/api/v1').body
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_api_version_no_path
|
21
|
+
app :rest_api do |r|
|
22
|
+
r.api(path: '') do
|
23
|
+
r.version 1 do
|
24
|
+
'api version 1'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
assert_equal 'api version 1', request.get('/v1').body
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_api_version_subdomain
|
32
|
+
app :rest_api do |r|
|
33
|
+
r.api(subdomain: 'api') do
|
34
|
+
r.version 1 do
|
35
|
+
'api version 1'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
assert_equal 'api version 1', request.get('/api/v1', "HTTP_HOST" => "api.example.com").body
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_api_host_fail
|
43
|
+
app :rest_api do |r|
|
44
|
+
r.api(subdomain: 'api') do
|
45
|
+
r.version 1 do
|
46
|
+
'api version 1'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
assert_equal 404, request.get('/api/v1', "HTTP_HOST" => "www.example.com").status
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_api_path_fail
|
54
|
+
app :rest_api do |r|
|
55
|
+
r.api do
|
56
|
+
r.version 1 do
|
57
|
+
'api version 1'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
assert_equal 404, request.get('/v1').status
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_api_path_fail2
|
65
|
+
app :rest_api do |r|
|
66
|
+
r.api do
|
67
|
+
r.resource :foo do |foo|
|
68
|
+
foo.list {[]}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
assert_equal 404, request.get('/foo').status
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiFormInputTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
app :rest_api do |r|
|
8
|
+
r.resource :albums do |rsc|
|
9
|
+
rsc.save { |atts| Album.create_or_update(atts) }
|
10
|
+
rsc.permit :name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_create
|
16
|
+
id, name = 1, 'bar'
|
17
|
+
album = Album.new(id, name)
|
18
|
+
response = request.post('/albums', params: {name: name})
|
19
|
+
assert_equal album.to_json, response.body
|
20
|
+
assert_equal 201, response.status
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_update
|
24
|
+
id, name = 12, 'foo'
|
25
|
+
album = Album.new(id, name)
|
26
|
+
assert_equal album.to_json, request.patch('/albums/12', params: {id: id, name: name}).body
|
27
|
+
assert_equal album.to_json, request.put('/albums/12', params: {id: id, name: name}).body
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiIdPatternTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
app :rest_api do |r|
|
8
|
+
r.api id_pattern: /id([\d]+)/ do
|
9
|
+
r.resource :artists, id_pattern: /@([\d]+)/ do |rsc|
|
10
|
+
rsc.one { |atts| "ARTIST_#{atts[:id]}" }
|
11
|
+
r.resource :songs, id_pattern: /foo([\d]+)/ do |rsc|
|
12
|
+
rsc.one { |atts| "SONG_#{atts[:id]}" }
|
13
|
+
end
|
14
|
+
r.resource :concerts do |rsc| # need to be explicit
|
15
|
+
rsc.one { |atts| "CONCERT_#{atts[:id]}" }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
r.resource :venues do |rsc|
|
19
|
+
rsc.one { |atts| "VENUE_#{atts[:id]}" }
|
20
|
+
end
|
21
|
+
r.resource :albums do |rsc|
|
22
|
+
rsc.save { |atts| "SAVE_ALBUM_#{atts[:id]}" }
|
23
|
+
rsc.routes :create, :update
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_complex_pattern
|
31
|
+
assert_equal "SAVE_ALBUM_12", request.put('api/albums/id12', params:{foo:'bar'}).body
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_failing_pattern
|
35
|
+
assert_equal 404, request.get('api/albums/12').status
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_pattern_override
|
39
|
+
assert_equal "ARTIST_12", request.get('api/artists/@12').body
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_nested
|
43
|
+
assert_equal "SONG_4", request.get('api/artists/@12/songs/foo4').body
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_nested_fallback
|
47
|
+
assert_equal "CONCERT_3", request.get('api/artists/@12/concerts/@3').body
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_fallback
|
51
|
+
assert_equal "VENUE_5", request.get('api/venues/id5').body
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiNestedTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
def setup
|
7
|
+
app :rest_api do |r|
|
8
|
+
r.resource :albums do |albums|
|
9
|
+
albums.list { |params| Album.find({} ) }
|
10
|
+
albums.one { |params| Album[params[:id]] }
|
11
|
+
albums.routes :index, :show
|
12
|
+
r.resource :songs do |songs|
|
13
|
+
songs.list { |params| Song.find({ :album_id => params[:parent_id]} ) }
|
14
|
+
songs.one { |params| Song[params[:id]] }
|
15
|
+
songs.routes :index, :show
|
16
|
+
end
|
17
|
+
r.resource :settings, singleton: true do |settings|
|
18
|
+
settings.one { |params| Settings[3] }
|
19
|
+
settings.save { |atts| Settings[3].save(atts) }
|
20
|
+
settings.routes :show, :update
|
21
|
+
settings.permit :name
|
22
|
+
end
|
23
|
+
r.resource :artwork, parent_key: :album_id do |artwork|
|
24
|
+
artwork.list { |params| Artwork.find(params) }
|
25
|
+
artwork.routes :index
|
26
|
+
end
|
27
|
+
r.resource :favorites, bare: true do |favorites|
|
28
|
+
favorites.list { |params| Favorite.find(params) }
|
29
|
+
favorites.one { |params| Favorite[params[:id]] }
|
30
|
+
favorites.routes :index, :show
|
31
|
+
r.resource :things do |things|
|
32
|
+
things.list { |params| Thing.find(params) }
|
33
|
+
things.one { |params| Thing[params[:id]] }
|
34
|
+
things.routes :index, :edit
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_album_index
|
44
|
+
assert_equal Album.find({}).to_json, request.get('/albums').body
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_album_show
|
48
|
+
assert_equal Album[10].to_json, request.get('/albums/10').body
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_album_fail
|
52
|
+
assert_equal 404, request.get('/albums/--').status
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_songs_index
|
56
|
+
assert_equal Song.find({:album_id => 12 }).to_json, request.get('/albums/12/songs').body
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_songs_show
|
60
|
+
assert_equal Song[10].to_json, request.get('/albums/12/songs/10').body
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_songs_fail
|
64
|
+
assert_equal 404, request.get('/albums/12/songs/--').status
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_singleton_update
|
68
|
+
id, name = 3, 'bar'
|
69
|
+
settings = Settings.new(id, name)
|
70
|
+
assert_equal settings.to_json, request.patch('/albums/12/settings', input: {name: name}.to_json).body
|
71
|
+
end
|
72
|
+
#
|
73
|
+
def test_favorites_index
|
74
|
+
assert_equal Favorite.find({}).to_json, request.get('/albums/favorites').body
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_favorites_show
|
78
|
+
assert_equal Favorite[7].to_json, request.get('/albums/favorites/7').body
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_filtered_default
|
82
|
+
assert_equal Song.find({:album_id => 9 }).to_json, request.get('/albums/9/songs').body
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_filtered_custom
|
86
|
+
assert_equal Artwork.find({:album_id => 8 }).to_json, request.get('/albums/8/artwork').body
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_deep_nest_fail
|
90
|
+
assert_equal 404, request.get('/albums/favorites/4/things/3').status
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_deep_nest
|
94
|
+
assert_equal Thing[3].to_json, request.get('/albums/favorites/things/3/edit').body
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
class Song < Mock ; end
|
99
|
+
class Settings < Mock ; end
|
100
|
+
class Favorite < Mock ; end
|
101
|
+
class Artwork < Mock ; end
|
102
|
+
class Thing < Mock ; end
|
103
|
+
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
require "minitest/benchmark"
|
3
|
+
|
4
|
+
class RestApiPerformanceTest < Minitest::Benchmark
|
5
|
+
include TestHelpers
|
6
|
+
|
7
|
+
def setup
|
8
|
+
app :rest_api do |r|
|
9
|
+
r.resource :albums do |rsc|
|
10
|
+
rsc.list { |params| Album.find(params) }
|
11
|
+
rsc.one { |params| Album[params[:id]] }
|
12
|
+
rsc.save { |atts| Album.create_or_update(atts) }
|
13
|
+
rsc.routes :show, :create
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def bench_show
|
19
|
+
assert_performance_linear 0.99 do |n|
|
20
|
+
n.times do
|
21
|
+
request.get('/albums/12').body
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def bench_create
|
27
|
+
assert_performance_linear 0.99 do |n|
|
28
|
+
n.times do
|
29
|
+
request.post('/albums', 'rack.input' => post_args({name: 'foo'})}).body
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "test_helpers"
|
2
|
+
|
3
|
+
class RestApiPermitTest < Minitest::Test
|
4
|
+
include TestHelpers
|
5
|
+
|
6
|
+
|
7
|
+
def test_index_permitted
|
8
|
+
app :rest_api do |r|
|
9
|
+
r.resource :albums do |albums|
|
10
|
+
albums.list { |params| Album.find(params) }
|
11
|
+
albums.routes :index
|
12
|
+
albums.permit :page
|
13
|
+
end
|
14
|
+
end
|
15
|
+
params = {page: '2'}
|
16
|
+
assert_equal Album.find(params).to_json, request.get('/albums', {'QUERY_STRING' => 'page=2'}).body
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def test_index_not_permitted
|
21
|
+
app :rest_api do |r|
|
22
|
+
r.resource :albums do |albums|
|
23
|
+
albums.list { |params| Album.find(params) }
|
24
|
+
albums.routes :index
|
25
|
+
albums.permit :page
|
26
|
+
end
|
27
|
+
end
|
28
|
+
params = {page_ct: '2'}
|
29
|
+
assert_equal Album.find({}).to_json, request.get('/albums', {'QUERY_STRING' => 'page_ct=2'}).body
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_create_permitted
|
33
|
+
app :rest_api do |r|
|
34
|
+
r.resource :albums do |albums|
|
35
|
+
albums.save { |params| Album.create_or_update(params) }
|
36
|
+
albums.routes :create, :update
|
37
|
+
albums.permit :name, :price
|
38
|
+
end
|
39
|
+
end
|
40
|
+
atts = {name: 'name', price: 3}
|
41
|
+
album = Album.new(1, atts[:name], atts[:price])
|
42
|
+
assert_equal album.to_json, request.post('/albums', input: atts.to_json).body
|
43
|
+
assert_equal 201, request.post('/albums', input: atts.to_json).status
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_create_not_permitted
|
47
|
+
app :rest_api do |r|
|
48
|
+
r.resource :albums do |albums|
|
49
|
+
albums.save { |params| Album.create_or_update(params) }
|
50
|
+
albums.routes :create, :update
|
51
|
+
albums.permit :name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
atts = {name: 'name', price: 3}
|
56
|
+
album = Album.new(1, atts[:name])
|
57
|
+
assert_equal album.to_json, request.post('/albums', input: atts.to_json).body
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_create_nested_single
|
61
|
+
app :rest_api do |r|
|
62
|
+
r.resource :albums do |albums|
|
63
|
+
albums.save { |params| Album.create_or_update(params) }
|
64
|
+
albums.routes :create, :update
|
65
|
+
albums.permit :name, {price: :amount}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
atts = {name: 'name', price: {amount: 4}}
|
70
|
+
album = Album.new(1, atts[:name], atts[:price])
|
71
|
+
assert_equal album.to_json, request.post('/albums', input: atts.to_json).body
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_create_nested_array
|
75
|
+
app :rest_api do |r|
|
76
|
+
r.resource :albums do |albums|
|
77
|
+
albums.save { |params| Album.create_or_update(params) }
|
78
|
+
albums.routes :create, :update
|
79
|
+
albums.permit :name, {price: [:amount, :currency, :discount]}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
atts = {name: 'name', price: {amount: 4}}
|
84
|
+
album = Album.new(1, atts[:name], atts[:price])
|
85
|
+
assert_equal album.to_json, request.post('/albums', input: atts.to_json).body
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_create_nested_single_not_permitted
|
89
|
+
app :rest_api do |r|
|
90
|
+
r.resource :albums do |albums|
|
91
|
+
albums.save { |params| Album.create_or_update(params) }
|
92
|
+
albums.routes :create, :update
|
93
|
+
albums.permit :name, {price: :total}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
atts = {name: 'name', price: {amount: 4}}
|
98
|
+
album = Album.new(1, atts[:name], {})
|
99
|
+
assert_equal album.to_json, request.post('/albums', input: atts.to_json).body
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_create_nested_array_not_permitted
|
103
|
+
app :rest_api do |r|
|
104
|
+
r.resource :albums do |albums|
|
105
|
+
albums.save { |params| Album.create_or_update(params) }
|
106
|
+
albums.routes :create, :update
|
107
|
+
albums.permit :name, {price: [:total, :currency, :discount]}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
atts = {name: 'name', price: {amount: 4, currency:'foo'}}
|
112
|
+
album = Album.new(1, atts[:name], {currency:'foo'})
|
113
|
+
assert_equal album.to_json, request.post('/albums', input: atts.to_json).body
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
end
|