committee 0.4.14 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/committee-stub +1 -1
- data/lib/committee/errors.rb +1 -10
- data/lib/committee/middleware/base.rb +9 -1
- data/lib/committee/middleware/request_validation.rb +5 -9
- data/lib/committee/middleware/response_validation.rb +3 -19
- data/lib/committee/middleware/stub.rb +4 -5
- data/lib/committee/request_validator.rb +28 -0
- data/lib/committee/response_generator.rb +19 -13
- data/lib/committee/response_validator.rb +21 -88
- data/lib/committee/router.rb +10 -9
- data/lib/committee/test/methods.rb +15 -17
- data/lib/committee.rb +10 -3
- data/test/middleware/request_validation_test.rb +23 -43
- data/test/middleware/response_validation_test.rb +9 -27
- data/test/middleware/stub_test.rb +10 -1
- data/test/request_validator_test.rb +34 -0
- data/test/response_generator_test.rb +15 -15
- data/test/response_validator_test.rb +19 -110
- data/test/router_test.rb +6 -6
- data/test/test/methods_test.rb +7 -20
- data/test/test_helper.rb +4 -40
- metadata +14 -17
- data/lib/committee/param_validator.rb +0 -106
- data/lib/committee/schema.rb +0 -56
- data/lib/committee/validation.rb +0 -83
- data/test/param_validator_test.rb +0 -127
- data/test/performance/request_validation.rb +0 -55
- data/test/schema_test.rb +0 -36
data/lib/committee/validation.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
module Committee
|
2
|
-
module Validation
|
3
|
-
def check_format!(format, value, identifier)
|
4
|
-
return if !format
|
5
|
-
return if check_format(format, value, identifier)
|
6
|
-
|
7
|
-
description = case identifier
|
8
|
-
when String
|
9
|
-
%{Invalid format for key "#{identifier}": expected "#{value}" to be "#{format}".}
|
10
|
-
when Array
|
11
|
-
%{Invalid format at "#{identifier.join(":")}": expected "#{value}" to be "#{format}".}
|
12
|
-
end
|
13
|
-
|
14
|
-
raise InvalidFormat, description
|
15
|
-
end
|
16
|
-
|
17
|
-
def check_format(format, value, identifier)
|
18
|
-
case format
|
19
|
-
when "date-time"
|
20
|
-
value =~ /^(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(\.\d{1,})?(Z|[+-](\d{2})\:(\d{2}))$/
|
21
|
-
when "email"
|
22
|
-
value =~ /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i
|
23
|
-
when "uuid"
|
24
|
-
value =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/
|
25
|
-
else
|
26
|
-
true
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def check_type!(allowed_types, value, identifier)
|
31
|
-
return if check_type(allowed_types, value, identifier)
|
32
|
-
|
33
|
-
description = case identifier
|
34
|
-
when String
|
35
|
-
%{Invalid type for key "#{identifier}": expected #{value.inspect} to be #{allowed_types}.}
|
36
|
-
when Array
|
37
|
-
%{Invalid type at "#{identifier.join(":")}": expected #{value.inspect} to be #{allowed_types}.}
|
38
|
-
end
|
39
|
-
|
40
|
-
raise InvalidType, description
|
41
|
-
end
|
42
|
-
|
43
|
-
def check_type(allowed_types, value, identifier)
|
44
|
-
types = case value
|
45
|
-
when NilClass
|
46
|
-
["null"]
|
47
|
-
when TrueClass, FalseClass
|
48
|
-
["boolean"]
|
49
|
-
when Bignum, Fixnum
|
50
|
-
["integer", "number"]
|
51
|
-
when Float
|
52
|
-
["number"]
|
53
|
-
when Hash
|
54
|
-
["object"]
|
55
|
-
when String
|
56
|
-
["string"]
|
57
|
-
when Array
|
58
|
-
["array"]
|
59
|
-
else
|
60
|
-
["unknown"]
|
61
|
-
end
|
62
|
-
|
63
|
-
!(allowed_types & types).empty?
|
64
|
-
end
|
65
|
-
|
66
|
-
def check_pattern!(pattern, value, identifier)
|
67
|
-
return if check_pattern(pattern, value, identifier)
|
68
|
-
|
69
|
-
description = case identifier
|
70
|
-
when String
|
71
|
-
%{Invalid pattern for key "#{identifier}": expected #{value} to match "#{pattern}".}
|
72
|
-
when Array
|
73
|
-
%{Invalid pattern at "#{identifier.join(":")}": expected #{value} to match "#{pattern}".}
|
74
|
-
end
|
75
|
-
|
76
|
-
raise InvalidPattern, description
|
77
|
-
end
|
78
|
-
|
79
|
-
def check_pattern(pattern, value, identifier)
|
80
|
-
!pattern || value =~ Regexp.new(pattern)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
require_relative "test_helper"
|
2
|
-
|
3
|
-
describe Committee::ParamValidator do
|
4
|
-
before do
|
5
|
-
@schema = Committee::Schema.new(File.read("./test/data/schema.json"))
|
6
|
-
# POST /account/app-transfers
|
7
|
-
@link_schema = @schema["app-transfer"]["links"][0]
|
8
|
-
end
|
9
|
-
|
10
|
-
it "passes through a valid request" do
|
11
|
-
params = {
|
12
|
-
"app" => "heroku-api",
|
13
|
-
"recipient" => "owner@heroku.com",
|
14
|
-
}
|
15
|
-
validate(params, @schema, @link_schema)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "detects a missing parameter" do
|
19
|
-
e = assert_raises(Committee::InvalidParams) do
|
20
|
-
validate({}, @schema, @link_schema)
|
21
|
-
end
|
22
|
-
message = "Require params: app, recipient."
|
23
|
-
assert_equal message, e.message
|
24
|
-
end
|
25
|
-
|
26
|
-
it "doesn't error on an extraneous parameter with allow_extra" do
|
27
|
-
params = {
|
28
|
-
"app" => "heroku-api",
|
29
|
-
"cloud" => "production",
|
30
|
-
"recipient" => "owner@heroku.com",
|
31
|
-
}
|
32
|
-
validate(params, @schema, @link_schema, allow_extra: true)
|
33
|
-
end
|
34
|
-
|
35
|
-
it "detects a parameter of the wrong type" do
|
36
|
-
params = {
|
37
|
-
"app" => "heroku-api",
|
38
|
-
"recipient" => 123,
|
39
|
-
}
|
40
|
-
e = assert_raises(Committee::InvalidType) do
|
41
|
-
validate(params, @schema, @link_schema)
|
42
|
-
end
|
43
|
-
message = %{Invalid type for key "recipient": expected 123 to be ["string"].}
|
44
|
-
assert_equal message, e.message
|
45
|
-
end
|
46
|
-
|
47
|
-
it "detects a parameter of the wrong format" do
|
48
|
-
params = {
|
49
|
-
"app" => "heroku-api",
|
50
|
-
"recipient" => "not-email",
|
51
|
-
}
|
52
|
-
e = assert_raises(Committee::InvalidFormat) do
|
53
|
-
validate(params, @schema, @link_schema)
|
54
|
-
end
|
55
|
-
message = %{Invalid format for key "recipient": expected "not-email" to be "email".}
|
56
|
-
assert_equal message, e.message
|
57
|
-
end
|
58
|
-
|
59
|
-
it "detects a parameter of the wrong pattern" do
|
60
|
-
params = {
|
61
|
-
"name" => "%@!"
|
62
|
-
}
|
63
|
-
link_schema = @schema["app"]["links"][0]
|
64
|
-
e = assert_raises(Committee::InvalidPattern) do
|
65
|
-
validate(params, @schema, link_schema)
|
66
|
-
end
|
67
|
-
message = %{Invalid pattern for key "name": expected %@! to match "(?-mix:^[a-z][a-z0-9-]{3,30}$)".}
|
68
|
-
assert_equal message, e.message
|
69
|
-
end
|
70
|
-
|
71
|
-
describe "complex arrays" do
|
72
|
-
it "passes an array parameter" do
|
73
|
-
params = {
|
74
|
-
"updates" => [
|
75
|
-
{ "name" => "bamboo", "state" => "private" },
|
76
|
-
{ "name" => "cedar", "state" => "public" },
|
77
|
-
]
|
78
|
-
}
|
79
|
-
link_schema = @schema["stack"]["links"][2]
|
80
|
-
validate(params, @schema, link_schema)
|
81
|
-
end
|
82
|
-
|
83
|
-
it "detects an array item with a parameter of the wrong type" do
|
84
|
-
params = {
|
85
|
-
"updates" => [
|
86
|
-
{ "name" => "bamboo", "state" => 123 },
|
87
|
-
]
|
88
|
-
}
|
89
|
-
link_schema = @schema["stack"]["links"][2]
|
90
|
-
e = assert_raises(Committee::InvalidType) do
|
91
|
-
validate(params, @schema, link_schema)
|
92
|
-
end
|
93
|
-
message = %{Invalid type for key "state": expected 123 to be ["string"].}
|
94
|
-
assert_equal message, e.message
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
describe "simple arrays" do
|
99
|
-
it "passes an array parameter" do
|
100
|
-
params = {
|
101
|
-
"password" => "1234",
|
102
|
-
"flags" => [ "vip", "customer" ]
|
103
|
-
}
|
104
|
-
link_schema = @schema["account"]["links"][1]
|
105
|
-
validate(params, @schema, link_schema)
|
106
|
-
end
|
107
|
-
|
108
|
-
it "detects an array item with a parameter of the wrong type" do
|
109
|
-
params = {
|
110
|
-
"password" => "1234",
|
111
|
-
"flags" => [ "vip", "customer", 999 ]
|
112
|
-
}
|
113
|
-
link_schema = @schema["account"]["links"][1]
|
114
|
-
e = assert_raises(Committee::InvalidType) do
|
115
|
-
validate(params, @schema, link_schema)
|
116
|
-
end
|
117
|
-
message = %{Invalid type for key "flags": expected 999 to be ["string"].}
|
118
|
-
assert_equal message, e.message
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
def validate(params, schema, link_schema, options = {})
|
125
|
-
Committee::ParamValidator.new(params, schema, link_schema, options).call
|
126
|
-
end
|
127
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require "benchmark"
|
2
|
-
require_relative "../test_helper"
|
3
|
-
|
4
|
-
describe Committee::Middleware::RequestValidation do
|
5
|
-
include Rack::Test::Methods
|
6
|
-
|
7
|
-
def app
|
8
|
-
@app
|
9
|
-
end
|
10
|
-
|
11
|
-
N = 50000
|
12
|
-
|
13
|
-
it "is reasonably quick" do
|
14
|
-
params = MultiJson.encode({
|
15
|
-
"app" => "heroku-api",
|
16
|
-
"recipient" => "owner@heroku.com",
|
17
|
-
})
|
18
|
-
|
19
|
-
Benchmark.benchmark(Benchmark::CAPTION, 7, Benchmark::FORMAT, ">overhead:") do |x|
|
20
|
-
raw = x.report("raw") do
|
21
|
-
N.times do
|
22
|
-
@app = new_rack_app(raw: true)
|
23
|
-
header "Content-Type", "application/json"
|
24
|
-
post "/account/app-transfers", params
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
rich = x.report("rich") do
|
29
|
-
N.times do
|
30
|
-
@app = new_rack_app
|
31
|
-
header "Content-Type", "application/json"
|
32
|
-
post "/account/app-transfers", params
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
puts "average overhead = #{(rich.real - raw.real) / N}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def new_rack_app(options = {})
|
43
|
-
Rack::Builder.new {
|
44
|
-
unless options.delete(:raw)
|
45
|
-
options = {
|
46
|
-
schema: File.read("./test/data/schema.json")
|
47
|
-
}.merge(options)
|
48
|
-
use Committee::Middleware::RequestValidation, options
|
49
|
-
end
|
50
|
-
run lambda { |_|
|
51
|
-
[200, {}, []]
|
52
|
-
}
|
53
|
-
}
|
54
|
-
end
|
55
|
-
end
|
data/test/schema_test.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require_relative "test_helper"
|
2
|
-
|
3
|
-
describe Committee::Schema do
|
4
|
-
before do
|
5
|
-
data = File.read("./test/data/schema.json")
|
6
|
-
@schema = Committee::Schema.new(data)
|
7
|
-
end
|
8
|
-
|
9
|
-
it "is enumerable" do
|
10
|
-
arr = @schema.to_a
|
11
|
-
assert_equal "account", arr[0][0]
|
12
|
-
assert arr[0][1].is_a?(Hash)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "can lookup definitions" do
|
16
|
-
expected = {
|
17
|
-
"default" => nil,
|
18
|
-
"description" => "slug size in bytes of app",
|
19
|
-
"example" => 0,
|
20
|
-
"readOnly" => true,
|
21
|
-
"type" => ["number", "null"]
|
22
|
-
}
|
23
|
-
assert_equal expected, @schema.find("#/definitions/app/definitions/slug_size")
|
24
|
-
end
|
25
|
-
|
26
|
-
it "raises error on a non-existent definition" do
|
27
|
-
assert_raises(Committee::ReferenceNotFound) do
|
28
|
-
@schema.find("/schema/app#/definitions/bad_field")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
it "materializes regexes" do
|
33
|
-
definition = @schema.find("#/definitions/app/definitions/git_url")
|
34
|
-
assert definition["pattern"].is_a?(Regexp)
|
35
|
-
end
|
36
|
-
end
|