sinatra_resource 0.3.2 → 0.3.3

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.
@@ -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
  #