committee 0.4.14 → 1.0.0
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.
- 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
|