apiculture 0.0.16 → 0.0.17
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/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
|