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 +4 -4
- data/apiculture.gemspec +4 -4
- data/lib/apiculture/version.rb +2 -2
- data/lib/apiculture.rb +25 -20
- data/spec/apiculture_spec.rb +27 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 917fb8bfc86e8bc1d1a9d02729434c6af8398c1b
|
4
|
+
data.tar.gz: 4c73cab2e1f0a8dfb2756175f1037f32a8336d0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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-
|
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
|
data/lib/apiculture/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Apiculture
|
2
|
-
VERSION = '0.0.
|
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
|
-
|
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
|
-
|
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
|
-
|
250
|
-
|
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
|
-
|
253
|
-
|
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,
|
257
|
-
# ..
|
258
|
-
|
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(¶metric_checker_proc)
|
261
266
|
# Execute the original action via instance_exec, passing along the route args
|
262
|
-
instance_exec(*
|
267
|
+
instance_exec(*route_parameters.values, &blk)
|
263
268
|
end
|
264
269
|
|
265
270
|
# Reset for the subsequent action definition
|
data/spec/apiculture_spec.rb
CHANGED
@@ -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.
|
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-
|
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
|