apiculture 0.0.16 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fddefec311625b3874fbcf1f2359f17d9393ae03
4
- data.tar.gz: 2006215622d1839c0316d1bd1c790b65b08e6fb5
3
+ metadata.gz: 917fb8bfc86e8bc1d1a9d02729434c6af8398c1b
4
+ data.tar.gz: 4c73cab2e1f0a8dfb2756175f1037f32a8336d0b
5
5
  SHA512:
6
- metadata.gz: 3933f460a2b5b2cd1ea3187d8b4023851efc82ad5b555b55decb0922751f198336998f59d16262ca522f2ef097c26e6978a063be122b842bf1ecd6977b7bacff
7
- data.tar.gz: b8f2c0ed089a32b45b5edeb98aea9fe1110b07a92f3df5aa0497c24ccb61e602fcfc547d1b4a0d011a631ca5b871c3ab198569dee3ec04b6b78538392e1ce1fd
6
+ metadata.gz: d0d6b8796a963cd7814bba03afc1904efeb23e901084eab438ce2af8a6379f5e7b5d1474f89cee53401e6d58d94c68be5290c8ed9677b169b9fd85df98cd4117
7
+ data.tar.gz: 33a2024a4918f561da343f6df0aa0876b1c458f5ffc9f02d1393284f64af5b5c06811cd65022cb26fa28d9407c332d52f048adcdafdbaa7f7bfc990c52a1eb58
data/apiculture.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: apiculture 0.0.16 ruby lib
5
+ # stub: apiculture 0.0.17 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "apiculture"
9
- s.version = "0.0.16"
9
+ s.version = "0.0.17"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Julik Tarkhanov", "WeTransfer"]
14
- s.date = "2016-09-27"
14
+ s.date = "2016-11-25"
15
15
  s.description = "A toolkit for building REST APIs on top of Sinatra"
16
16
  s.email = "me@julik.nl"
17
17
  s.extra_rdoc_files = [
@@ -43,7 +43,7 @@ Gem::Specification.new do |s|
43
43
  ]
44
44
  s.homepage = "https://github.com/WeTransfer/apiculture"
45
45
  s.licenses = ["MIT"]
46
- s.rubygems_version = "2.5.1"
46
+ s.rubygems_version = "2.4.5.1"
47
47
  s.summary = "Sweet API sauce on top of Sintra"
48
48
 
49
49
  if s.respond_to? :specification_version then
@@ -1,3 +1,3 @@
1
1
  module Apiculture
2
- VERSION = '0.0.16'
3
- end
2
+ VERSION = '0.0.17'
3
+ end
data/lib/apiculture.rb CHANGED
@@ -26,10 +26,6 @@ module Apiculture
26
26
  param.matchable === value or raise ParameterTypeMismatch.new(param, value.class)
27
27
  }
28
28
 
29
- AC_PERMIT_PROC = ->(maybe_strong_params, param_name) {
30
- maybe_strong_params.permit(param_name) if maybe_strong_params.respond_to?(:permit)
31
- }
32
-
33
29
  class Parameter < Struct.new(:name, :description, :required, :matchable, :cast_proc_or_method)
34
30
  # Return Strings since Sinatra prefers string keys for params{}
35
31
  def name_as_string; name.to_s; end
@@ -154,7 +150,7 @@ module Apiculture
154
150
  end
155
151
 
156
152
  # Returns a Proc that calls the strong parameters to check the presence/types
157
- def parametric_validator_proc_from(parametric_validators)
153
+ def parametric_validator_proc_from(parametric_validators, implicitly_defined_route_parameter_names)
158
154
  required_params = parametric_validators.select{|e| e.required }
159
155
  # Return a lambda that will be called with the Sinatra params
160
156
  parametric_validation_blk = ->{
@@ -171,14 +167,16 @@ module Apiculture
171
167
 
172
168
  # Ensure the typecast value adheres to the enforced Ruby type
173
169
  AC_CHECK_TYPE_PROC.call(param, params[param_name])
174
- # ..permit it in the strong parameters if we support them
175
- AC_PERMIT_PROC.call(params, param_name)
176
170
  end
177
171
 
178
172
  # The following only applies if the app does not use strong_parameters -
179
173
  # this makes use of parameter mutability again to kill the parameters that are not permitted
180
- # or mentioned in the API specification
181
- unexpected_parameters = params.keys.map(&:to_s) - parametric_validators.map(&:name).map(&:to_s)
174
+ # or mentioned in the API specification. We need to keep the params which are specified in the
175
+ # route but not documented via Apiculture though
176
+ unexpected_parameters = Set.new(params.keys.map(&:to_s)) -
177
+ Set.new(parametric_validators.map(&:name).map(&:to_s)) -
178
+ Set.new(implicitly_defined_route_parameter_names.map(&:to_s))
179
+
182
180
  unexpected_parameters.each do | parameter_to_discard |
183
181
  # TODO: raise or record a warning
184
182
  if env['rack.logger'].respond_to?(:warn)
@@ -243,23 +241,30 @@ module Apiculture
243
241
 
244
242
  # Pick out all the defined parameters and set up a block that can validate them
245
243
  # when the action is called. With that, set up the actual Sinatra method that will
246
- # respond to the request.
247
- parametric_checker_proc = parametric_validator_proc_from(action_def.parameters + action_def.route_parameters)
244
+ # respond to the request. We take care to preserve all the params that have NOT been documented
245
+ # using Apiculture but _were_ in fact specified in the actual path.
246
+ route_parameter_names = path.scan(/:([^:\/]+)/).flatten.map(&:to_sym)
247
+ parametric_checker_proc = parametric_validator_proc_from(action_def.parameters + action_def.route_parameters, route_parameter_names)
248
248
  public_send(http_verb, path, options) do |*matched_sinatra_route_params|
249
- route_params = []
250
- action_def.route_parameters.each_with_index do |route_param, index|
249
+ # Extract all the parameter names from the route path as given to the method
250
+ route_parameters = Hash[route_parameter_names.zip(matched_sinatra_route_params)]
251
+
252
+ # Apply route parameter checks, but only to params that were defined in the Apiculture action descriptor.
253
+ # All the other params have to go via bypass.
254
+ checked_route_parameters = action_def.route_parameters.select {|par| route_parameter_names.include?(par.name) }
255
+ checked_route_parameters.each do |route_param|
251
256
  # Apply the type cast and save it (since using our override we can mutate the params)
252
- value_after_type_cast = AC_APPLY_TYPECAST_PROC.call(route_param.cast_proc_or_method, params[route_param.name])
253
- route_params[index] = value_after_type_cast
254
-
257
+ value_from_route_params = route_parameters.fetch(route_param.name)
258
+ value_after_type_cast = AC_APPLY_TYPECAST_PROC.call(route_param.cast_proc_or_method, value_from_route_params)
255
259
  # Ensure the typecast value adheres to the enforced Ruby type
256
- AC_CHECK_TYPE_PROC.call(route_param, route_params[index])
257
- # ..permit it in the strong parameters if we support them
258
- AC_PERMIT_PROC.call(route_params, route_param.name)
260
+ AC_CHECK_TYPE_PROC.call(route_param, value_after_type_cast)
261
+ # ..and overwrite it in the route parameters hash
262
+ route_parameters[route_param.name] = value_after_type_cast
259
263
  end
264
+ # Execute parametric checks on all the OTHER params (forms etc.)
260
265
  instance_exec(&parametric_checker_proc)
261
266
  # Execute the original action via instance_exec, passing along the route args
262
- instance_exec(*route_params, &blk)
267
+ instance_exec(*route_parameters.values, &blk)
263
268
  end
264
269
 
265
270
  # Reset for the subsequent action definition
@@ -191,7 +191,33 @@ describe "Apiculture" do
191
191
  post '/thing', {evil_ssh_injection: 'I am Homakov!'}
192
192
  expect(last_response).to be_ok
193
193
  end
194
-
194
+
195
+ it 'allows route parameters that are not mentioned in the action definition, but are given in Sinatra path' do
196
+ @app_class = Class.new(Sinatra::Base) do
197
+ settings.show_exceptions = false
198
+ settings.raise_errors = true
199
+ extend Apiculture
200
+
201
+ api_method :post, '/api-thing/:id_of_thing' do |id|
202
+ raise 'id_of_thing must be passed' unless id == '123456'
203
+ raise "id_of_thing must be present in params, but they were #{params.inspect}" unless params.keys.include?('id_of_thing')
204
+ 'All is well'
205
+ end
206
+
207
+ post '/vanilla-thing/:id_of_thing' do |id|
208
+ raise 'id_of_thing must be passed' unless id == '123456'
209
+ raise "id_of_thing must be present in params, but they were #{params.inspect}" unless params.keys.include?('id_of_thing')
210
+ 'All is well'
211
+ end
212
+ end
213
+
214
+ post '/vanilla-thing/123456'
215
+ expect(last_response).to be_ok
216
+
217
+ post '/api-thing/123456'
218
+ expect(last_response).to be_ok
219
+ end
220
+
195
221
  it 'raises when describing a route parameter that is not included in the path' do
196
222
  expect {
197
223
  Class.new(Sinatra::Base) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apiculture
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.16
4
+ version: 0.0.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-09-27 00:00:00.000000000 Z
12
+ date: 2016-11-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
@@ -220,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
220
  version: '0'
221
221
  requirements: []
222
222
  rubyforge_project:
223
- rubygems_version: 2.5.1
223
+ rubygems_version: 2.4.5.1
224
224
  signing_key:
225
225
  specification_version: 4
226
226
  summary: Sweet API sauce on top of Sintra