sinatra_resource 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,89 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
2
+
3
+ class SourcesUsagesGetOneResourceTest < ResourceTestCase
4
+
5
+ include DataCatalog
6
+
7
+ def app; Sources end
8
+
9
+ before do
10
+ @source = create_source
11
+ @source.usages << new_usage
12
+ @source.save
13
+ @usage_id = @source.usages[0].id
14
+
15
+ @other_source = create_source
16
+ @other_source.usages << new_usage
17
+ @other_source.save
18
+ @other_usage_id = @other_source.usages[0].id
19
+ end
20
+
21
+ after do
22
+ @other_source.destroy
23
+ @source.destroy
24
+ end
25
+
26
+ context "get /:id/usages/:id" do
27
+ context "anonymous" do
28
+ before do
29
+ get "/#{@source.id}/usages/#{@usage_id}"
30
+ end
31
+
32
+ use "return 401 because the API key is missing"
33
+ end
34
+
35
+ context "incorrect API key" do
36
+ before do
37
+ get "/#{@source.id}/usages/#{@usage_id}", :api_key => BAD_API_KEY
38
+ end
39
+
40
+ use "return 401 because the API key is invalid"
41
+ end
42
+ end
43
+
44
+ %w(basic).each do |role|
45
+ # %w(basic curator admin).each do |role|
46
+ context "#{role} : get /:fake_id/usages/:fake_id" do
47
+ before do
48
+ get "/#{FAKE_ID}/usages/#{FAKE_ID}", :api_key => api_key_for(role)
49
+ end
50
+
51
+ use "return 404 Not Found with empty response body"
52
+ end
53
+
54
+ context "#{role} : get /:fake_id/usages/:id" do
55
+ before do
56
+ get "/#{FAKE_ID}/usages/#{@usage_id}", :api_key => api_key_for(role)
57
+ end
58
+
59
+ use "return 404 Not Found with empty response body"
60
+ end
61
+
62
+ context "#{role} : get /:id/usages/:fake_id" do
63
+ before do
64
+ get "/#{@source.id}/usages/#{FAKE_ID}", :api_key => api_key_for(role)
65
+ end
66
+
67
+ use "return 404 Not Found with empty response body"
68
+ end
69
+
70
+ context "#{role} : get /:id/usages/:not_related_id" do
71
+ before do
72
+ get "/#{@source.id}/usages/#{@other_usage_id}",
73
+ :api_key => api_key_for(role)
74
+ end
75
+
76
+ use "return 404 Not Found with empty response body"
77
+ end
78
+
79
+ context "#{role} : get /:id/usages/:id" do
80
+ before do
81
+ get "/#{@source.id}/usages/#{@usage_id}", :api_key => api_key_for(role)
82
+ end
83
+
84
+ use "return 200 Ok"
85
+ doc_properties %w(title url description id)
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,124 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
2
+
3
+ class SourcesUsagesPostResourceTest < ResourceTestCase
4
+
5
+ include DataCatalog
6
+
7
+ def app; Sources end
8
+
9
+ before do
10
+ @source = create_source
11
+ @usage_count = @source.usages.length
12
+ @valid_params = {
13
+ :title => "Sample Usage",
14
+ :url => "http://inter.net/article/100",
15
+ }
16
+ end
17
+
18
+ after do
19
+ @source.destroy
20
+ end
21
+
22
+ context "post /:id/usages" do
23
+ context "anonymous" do
24
+ before do
25
+ post "/#{@source.id}/usages"
26
+ end
27
+
28
+ use "return 401 because the API key is missing"
29
+ use "no change in usage count"
30
+ end
31
+
32
+ context "incorrect API key" do
33
+ before do
34
+ post "/#{@source.id}/usages", :api_key => BAD_API_KEY
35
+ end
36
+
37
+ use "return 401 because the API key is invalid"
38
+ use "no change in usage count"
39
+ end
40
+ end
41
+
42
+ %w(basic).each do |role|
43
+ context "#{role} : post /:fake_id/usages" do
44
+ before do
45
+ post "/#{FAKE_ID}/usages", :api_key => api_key_for(role)
46
+ end
47
+
48
+ use "return 404 Not Found with empty response body"
49
+ use "no change in usage count"
50
+ end
51
+
52
+ context "#{role} : post /:id/usages" do
53
+ before do
54
+ post "/#{@source.id}/usages", :api_key => api_key_for(role)
55
+ end
56
+
57
+ use "return 401 because the API key is unauthorized"
58
+ use "no change in usage count"
59
+ end
60
+ end
61
+
62
+ %w(curator admin).each do |role|
63
+ context "#{role} : post /:fake_id/usages" do
64
+ before do
65
+ post "/#{FAKE_ID}/usages", :api_key => api_key_for(role)
66
+ end
67
+
68
+ use "return 404 Not Found with empty response body"
69
+ use "no change in usage count"
70
+ end
71
+
72
+ # TODO: As of 2009-10-29, MongoMapper does not support validations on
73
+ # EmbeddedDocuments.
74
+ #
75
+ # [:title, :url].each do |missing|
76
+ # context "#{role} : post /:id/usages but missing #{missing}" do
77
+ # before do
78
+ # post "/#{@source.id}/usages",
79
+ # valid_params_for(role).delete_if { |k, v| k == missing }
80
+ # end
81
+ #
82
+ # use "return 400 Bad Request"
83
+ # use "no change in usage count"
84
+ # missing_param missing
85
+ # end
86
+ # end
87
+
88
+ [:junk].each do |invalid|
89
+ context "#{role} : post /:id/usages/ but with #{invalid}" do
90
+ before do
91
+ post "/#{@source.id}/usages", valid_params_for(role).
92
+ merge(invalid => 9)
93
+ end
94
+
95
+ use "return 400 Bad Request"
96
+ use "no change in usage count"
97
+ invalid_param invalid
98
+ end
99
+ end
100
+
101
+ context "#{role} : post /:id/usages with valid params" do
102
+ before do
103
+ post "/#{@source.id}/usages", valid_params_for(role)
104
+ end
105
+
106
+ use "return 201 Created"
107
+ nested_location_header "sources", :source, "usages"
108
+ use "one new usage"
109
+ doc_properties %w(title url description id)
110
+
111
+ test "new usage created correctly" do
112
+ source = Source.find_by_id(@source.id)
113
+ # TODO: use reload instead
114
+ usages = source.usages
115
+ assert_equal 1, usages.length
116
+ usage = usages[0]
117
+ @valid_params.each_pair do |key, value|
118
+ assert_equal value, usage[key]
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ end
@@ -0,0 +1,222 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../helpers/resource_test_helper')
2
+
3
+ class SourcesUsagesPutResourceTest < ResourceTestCase
4
+
5
+ include DataCatalog
6
+
7
+ def app; Sources end
8
+
9
+ before do
10
+ @source = create_source
11
+ @source.usages << new_usage
12
+ @source.save
13
+ @usage_copy = @source.usages[0]
14
+ @usage_id = @usage_copy.id
15
+
16
+ @other_source = create_source
17
+ @other_source.usages << new_usage
18
+ @other_source.save
19
+ @other_usage_id = @other_source.usages[0].id
20
+
21
+ @usage_count = @source.usages.length
22
+ @valid_params = {
23
+ :title => "Sample Usage",
24
+ :url => "http://inter.net/article/100",
25
+ }
26
+ end
27
+
28
+ after do
29
+ @other_source.destroy
30
+ @source.destroy
31
+ end
32
+
33
+ context "put /:id/usages/:id" do
34
+ context "anonymous" do
35
+ before do
36
+ put "/#{@source.id}/usages/#{@usage_id}"
37
+ end
38
+
39
+ use "return 401 because the API key is missing"
40
+ use "usage unchanged"
41
+ end
42
+
43
+ context "incorrect API key" do
44
+ before do
45
+ put "/#{@source.id}/usages/#{@usage_id}", :api_key => BAD_API_KEY
46
+ end
47
+
48
+ use "return 401 because the API key is invalid"
49
+ use "usage unchanged"
50
+ end
51
+ end
52
+
53
+ %w(basic).each do |role|
54
+ context "#{role} : put /:fake_id/usages/:fake_id" do
55
+ before do
56
+ put "/#{FAKE_ID}/usages/#{FAKE_ID}", :api_key => api_key_for(role)
57
+ end
58
+
59
+ use "return 404 Not Found with empty response body"
60
+ use "usage unchanged"
61
+ end
62
+
63
+ context "#{role} : put /:fake_id/usages/:id" do
64
+ before do
65
+ put "/#{FAKE_ID}/usages/#{@usage_id}", :api_key => api_key_for(role)
66
+ end
67
+
68
+ use "return 404 Not Found with empty response body"
69
+ use "usage unchanged"
70
+ end
71
+
72
+ context "#{role} : put /:id/usages/:fake_id" do
73
+ before do
74
+ put "/#{@source.id}/usages/#{FAKE_ID}", :api_key => api_key_for(role)
75
+ end
76
+
77
+ use "return 401 because the API key is unauthorized"
78
+ use "usage unchanged"
79
+ end
80
+
81
+ context "#{role} : put /:id/usages/:not_related_id" do
82
+ before do
83
+ put "/#{@source.id}/usages/#{@other_usage_id}",
84
+ :api_key => api_key_for(role)
85
+ end
86
+
87
+ use "return 401 because the API key is unauthorized"
88
+ use "usage unchanged"
89
+ end
90
+
91
+ context "#{role} : put /:id/usages/:id" do
92
+ before do
93
+ put "/#{@source.id}/usages/#{@usage_id}", :api_key => api_key_for(role)
94
+ end
95
+
96
+ use "return 401 because the API key is unauthorized"
97
+ use "usage unchanged"
98
+ end
99
+ end
100
+
101
+ %w(curator admin).each do |role|
102
+ context "#{role} : put /:fake_id/usages/:fake_id" do
103
+ before do
104
+ put "/#{FAKE_ID}/usages/#{FAKE_ID}", :api_key => api_key_for(role)
105
+ end
106
+
107
+ use "return 404 Not Found with empty response body"
108
+ use "usage unchanged"
109
+ end
110
+
111
+ context "#{role} : put /:fake_id/usages/:id" do
112
+ before do
113
+ put "/#{FAKE_ID}/usages/#{@usage_id}", :api_key => api_key_for(role)
114
+ end
115
+
116
+ use "return 404 Not Found with empty response body"
117
+ use "usage unchanged"
118
+ end
119
+
120
+ context "#{role} : put /:id/usages/:fake_id" do
121
+ before do
122
+ put "/#{@source.id}/usages/#{FAKE_ID}", :api_key => api_key_for(role)
123
+ end
124
+
125
+ use "return 404 Not Found with empty response body"
126
+ use "usage unchanged"
127
+ end
128
+
129
+ context "#{role} : put /:id/usages/:not_related_id" do
130
+ before do
131
+ put "/#{@source.id}/usages/#{@other_usage_id}",
132
+ :api_key => api_key_for(role)
133
+ end
134
+
135
+ use "return 404 Not Found with empty response body"
136
+ use "usage unchanged"
137
+ end
138
+
139
+ context "#{role} : put /:id/usages/:id with no params" do
140
+ before do
141
+ put "/#{@source.id}/usages/#{@usage_id}",
142
+ :api_key => api_key_for(role)
143
+ end
144
+
145
+ use "return 400 because no params were given"
146
+ use "usage unchanged"
147
+ end
148
+
149
+ # TODO: As of 2009-10-29, MongoMapper does not support validations on
150
+ # EmbeddedDocuments.
151
+ #
152
+ # [:title, :url].each do |missing|
153
+ # context "#{role} : put /:id/usages/:id without #{missing}" do
154
+ # before do
155
+ # put "/#{@source.id}/usages/#{@usage_id}",
156
+ # valid_params_for(role).delete_if { |k, v| k == missing }
157
+ # end
158
+ #
159
+ # use "return 200 Ok"
160
+ # doc_properties %w(title url description id)
161
+ #
162
+ # test "should change correct fields in database" do
163
+ # usage = @source.usages[0]
164
+ # @valid_params.each_pair do |key, value|
165
+ # assert_equal(value, usage[key]) if key != missing
166
+ # end
167
+ # assert_equal @usage_copy[missing], usage[missing]
168
+ # end
169
+ # end
170
+ # end
171
+
172
+ # TODO: As of 2009-10-29, MongoMapper does not support validations on
173
+ # EmbeddedDocuments.
174
+ #
175
+ # [:title, :url].each do |erase|
176
+ # context "#{role} : put /:id/usages/:id but blanking out #{erase}" do
177
+ # before do
178
+ # put "/#{@source.id}/usages/#{@usage_id}",
179
+ # valid_params_for(role).merge(erase => "")
180
+ # end
181
+ #
182
+ # use "return 400 Bad Request"
183
+ # use "usage unchanged"
184
+ # missing_param erase
185
+ # end
186
+ # end
187
+
188
+ [:junk].each do |invalid|
189
+ context "#{role} : put /:id/usages/:id with #{invalid}" do
190
+ before do
191
+ put "/#{@source.id}/usages/#{@usage_id}",
192
+ valid_params_for(role).merge(invalid => 9)
193
+ end
194
+
195
+ use "return 400 Bad Request"
196
+ use "usage unchanged"
197
+ invalid_param invalid
198
+ end
199
+ end
200
+
201
+ context "#{role} : put /:id/usages/:id with valid params" do
202
+ before do
203
+ put "/#{@source.id}/usages/#{@usage_id}", valid_params_for(role)
204
+ end
205
+
206
+ use "return 200 Ok"
207
+ doc_properties %w(title url description id)
208
+
209
+ test "should change correct fields in database" do
210
+ source = Source.find_by_id(@source.id)
211
+ # TODO: use reload instead
212
+ usages = source.usages
213
+ assert_equal 1, usages.length
214
+ usage = usages[0]
215
+ @valid_params.each_pair do |key, value|
216
+ assert_equal value, usage[key]
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ end
@@ -4,34 +4,41 @@ module SinatraResource
4
4
 
5
5
  module ActionDefinitions
6
6
 
7
- def document_for_get_one(role, model, resource_config, leaf, id, parent_document, association)
7
+ def document_for_get_one(role, model, resource_config, leaf, id, parent_document, child_assoc)
8
8
  check_permission(:read, role, resource_config)
9
9
  if resource_config[:parent]
10
- check_related?(parent_document, association, id)
10
+ check_related?(parent_document, child_assoc, id)
11
11
  end
12
12
  check_params(:read, role, resource_config, leaf)
13
- find_document!(model, id)
13
+ if resource_config[:parent]
14
+ find_nested_document!(parent_document, child_assoc, model, id)
15
+ else
16
+ find_document!(model, id)
17
+ end
14
18
  end
15
19
 
16
- def documents_for_get_many(role, model, resource_config, leaf, parent_document, association)
20
+ def documents_for_get_many(role, model, resource_config, leaf, parent_document, child_assoc)
17
21
  check_permission(:list, role, resource_config)
18
22
  check_params(:list, role, resource_config, leaf)
19
- documents = find_documents!(model).select do |document|
20
- authorized?(:read, lookup_role(document), resource_config)
23
+ documents = if resource_config[:parent]
24
+ find_nested_documents!(parent_document, child_assoc, model)
25
+ else
26
+ find_documents!(model)
21
27
  end
22
- # TODO: A more performant approach would be to modify find_documents!
23
- # so that it returns the correct results in one query.
24
- if resource_config[:parent]
25
- documents = select_related(parent_document, association, documents)
28
+ documents.select do |doc|
29
+ authorized?(:read, lookup_role(doc), resource_config)
26
30
  end
27
- documents
28
31
  end
29
32
 
30
- def document_for_post(role, model, resource_config, leaf, parent_document, association)
33
+ def document_for_post(role, model, resource_config, leaf, parent_document, child_assoc)
31
34
  check_permission(:create, role, resource_config)
32
35
  check_params(:create, role, resource_config, leaf)
33
36
  do_callback(:before_create, resource_config, nil)
34
- document = create_document!(model)
37
+ document = if resource_config[:parent]
38
+ create_nested_document!(parent_document, child_assoc, model)
39
+ else
40
+ create_document!(model)
41
+ end
35
42
  if resource_config[:parent]
36
43
  make_related(parent_document, document, resource_config)
37
44
  end
@@ -39,26 +46,44 @@ module SinatraResource
39
46
  document
40
47
  end
41
48
 
42
- def document_for_put(role, model, resource_config, leaf, id, parent_document, association)
49
+ def document_for_put(role, model, resource_config, leaf, id, parent_document, child_assoc)
43
50
  check_permission(:update, role, resource_config)
44
51
  if resource_config[:parent]
45
- check_related?(parent_document, association, id)
52
+ check_related?(parent_document, child_assoc, id)
46
53
  end
47
54
  check_params(:update, role, resource_config, leaf)
48
- do_callback(:before_update, resource_config, find_document!(model, id))
49
- document = update_document!(model, id)
55
+ document = if resource_config[:parent]
56
+ find_nested_document!(parent_document, child_assoc, model, id)
57
+ else
58
+ find_document!(model, id)
59
+ end
60
+ do_callback(:before_update, resource_config, document)
61
+ document = if resource_config[:parent]
62
+ update_nested_document!(parent_document, child_assoc, model, id)
63
+ else
64
+ update_document!(model, id)
65
+ end
50
66
  do_callback(:after_update, resource_config, document)
51
67
  document
52
68
  end
53
69
 
54
- def document_for_delete(role, model, resource_config, leaf, id, parent_document, association)
70
+ def document_for_delete(role, model, resource_config, leaf, id, parent_document, child_assoc)
55
71
  check_permission(:delete, role, resource_config)
56
72
  if resource_config[:parent]
57
- check_related?(parent_document, association, id)
73
+ check_related?(parent_document, child_assoc, id)
58
74
  end
59
75
  check_params(:delete, role, resource_config, leaf)
60
- do_callback(:before_destroy, resource_config, find_document!(model, id))
61
- document = delete_document!(model, id)
76
+ document = if resource_config[:parent]
77
+ find_nested_document!(parent_document, child_assoc, model, id)
78
+ else
79
+ find_document!(model, id)
80
+ end
81
+ do_callback(:before_destroy, resource_config, document)
82
+ document = if resource_config[:parent]
83
+ delete_nested_document!(parent_document, child_assoc, model, id)
84
+ else
85
+ delete_document!(model, id)
86
+ end
62
87
  do_callback(:after_destroy, resource_config, document)
63
88
  document
64
89
  end
@@ -105,14 +105,14 @@ module SinatraResource
105
105
  # @param [Hash] resource_config
106
106
  #
107
107
  # @return [String]
108
- def display(action, object, resource_config)
108
+ def display(action, object, resource_config, parent_id = nil)
109
109
  case action
110
110
  when :list
111
111
  when :read
112
112
  when :create
113
113
  response.status = 201
114
- path = resource_config[:path] + %(/#{object["id"]})
115
- response.headers['Location'] = full_uri(path)
114
+ response.headers['Location'] = location(object, resource_config,
115
+ parent_id)
116
116
  when :update
117
117
  when :delete
118
118
  response.status = 204
@@ -143,16 +143,36 @@ module SinatraResource
143
143
  raise NotImplementedError
144
144
  end
145
145
 
146
- # Get role, using +id+ if specified. Delegates to +lookup_role+.
146
+ # Get role, using +model+ and +id+. Delegates to +lookup_role+.
147
147
  #
148
148
  # When +id+ is present, it can help determine 'relative' roles such
149
149
  # as 'ownership' of the current user of a particular document.
150
150
  #
151
- # @param [String, nil] id
151
+ # @param [Class] model
152
+ #
153
+ # @param [String] id
152
154
  #
153
155
  # @return [Symbol]
154
- def get_role(model, id=nil)
155
- lookup_role(id ? model.find_by_id(id) : nil)
156
+ def role_for(model, id)
157
+ lookup_role(model.find_by_id(id))
158
+ end
159
+
160
+ # Get role for a nested resource situation. Delegates to +lookup_role+.
161
+ #
162
+ # @params [MongoMapper::Document] parent
163
+ # The parent document
164
+ #
165
+ # @params [Symbol] child_assoc
166
+ # Association from the parent to the child
167
+ #
168
+ # @params [Class] child_model
169
+ #
170
+ # @params [String] child_id
171
+ #
172
+ # @return [Symbol]
173
+ def role_for_nested(parent, child_assoc, child_model, child_id)
174
+ lookup_role(
175
+ find_nested_document(parent, child_assoc, child_model, child_id))
156
176
  end
157
177
 
158
178
  # Return the minimum role required for +action+, and, if specified,
@@ -233,7 +253,7 @@ module SinatraResource
233
253
  when :errors
234
254
  { "errors" => object }
235
255
  when :internal_server_error
236
- ""
256
+ { "errors" => "internal_server_error" }
237
257
  when :invalid_document
238
258
  { "errors" => object.errors.errors }
239
259
  when :invalid_params
@@ -249,7 +269,30 @@ module SinatraResource
249
269
  end
250
270
  end
251
271
 
252
- # Lookup the rol, using +document+ if specified.
272
+ # Return a full URI for +object+.
273
+ #
274
+ # @param [Object] object
275
+ # A resource or a list of resources
276
+ #
277
+ # @param [Hash] resource_config
278
+ #
279
+ # @param [String] parent_id
280
+ #
281
+ # @return [String]
282
+ def location(object, resource_config, parent_id)
283
+ o = object
284
+ c = resource_config
285
+ path = if c[:parent]
286
+ raise Error, "expecting parent_id" unless parent_id
287
+ pc = c[:parent].resource_config
288
+ pc[:path] + '/' + parent_id + '/' + c[:path] + '/' + o["id"]
289
+ else
290
+ c[:path] + '/' + o["id"]
291
+ end
292
+ full_uri(path)
293
+ end
294
+
295
+ # Lookup the role, using +document+ if specified.
253
296
  #
254
297
  # Applications must override this method.
255
298
  #