committee 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/bin/committee-stub +1 -0
  3. data/lib/committee.rb +12 -34
  4. data/lib/committee/bin/committee_stub.rb +6 -4
  5. data/lib/committee/drivers.rb +15 -67
  6. data/lib/committee/drivers/driver.rb +47 -0
  7. data/lib/committee/drivers/hyper_schema.rb +8 -171
  8. data/lib/committee/drivers/hyper_schema/driver.rb +105 -0
  9. data/lib/committee/drivers/hyper_schema/link.rb +68 -0
  10. data/lib/committee/drivers/hyper_schema/schema.rb +22 -0
  11. data/lib/committee/drivers/open_api_2.rb +9 -416
  12. data/lib/committee/drivers/open_api_2/driver.rb +253 -0
  13. data/lib/committee/drivers/open_api_2/header_schema_builder.rb +33 -0
  14. data/lib/committee/drivers/open_api_2/link.rb +36 -0
  15. data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +83 -0
  16. data/lib/committee/drivers/open_api_2/schema.rb +26 -0
  17. data/lib/committee/drivers/open_api_2/schema_builder.rb +33 -0
  18. data/lib/committee/drivers/open_api_3.rb +7 -75
  19. data/lib/committee/drivers/open_api_3/driver.rb +51 -0
  20. data/lib/committee/drivers/open_api_3/schema.rb +41 -0
  21. data/lib/committee/drivers/schema.rb +23 -0
  22. data/lib/committee/errors.rb +2 -0
  23. data/lib/committee/middleware.rb +11 -0
  24. data/lib/committee/middleware/base.rb +38 -34
  25. data/lib/committee/middleware/request_validation.rb +51 -30
  26. data/lib/committee/middleware/response_validation.rb +49 -26
  27. data/lib/committee/middleware/stub.rb +55 -51
  28. data/lib/committee/request_unpacker.rb +3 -1
  29. data/lib/committee/schema_validator.rb +23 -0
  30. data/lib/committee/schema_validator/hyper_schema.rb +85 -74
  31. data/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb +60 -54
  32. data/lib/committee/schema_validator/hyper_schema/request_validator.rb +43 -37
  33. data/lib/committee/schema_validator/hyper_schema/response_generator.rb +86 -80
  34. data/lib/committee/schema_validator/hyper_schema/response_validator.rb +65 -59
  35. data/lib/committee/schema_validator/hyper_schema/router.rb +35 -29
  36. data/lib/committee/schema_validator/hyper_schema/string_params_coercer.rb +87 -81
  37. data/lib/committee/schema_validator/open_api_3.rb +71 -61
  38. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +121 -115
  39. data/lib/committee/schema_validator/open_api_3/request_validator.rb +24 -18
  40. data/lib/committee/schema_validator/open_api_3/response_validator.rb +22 -16
  41. data/lib/committee/schema_validator/open_api_3/router.rb +30 -24
  42. data/lib/committee/schema_validator/option.rb +42 -38
  43. data/lib/committee/test/methods.rb +55 -51
  44. data/lib/committee/validation_error.rb +2 -0
  45. data/test/bin/committee_stub_test.rb +3 -1
  46. data/test/bin_test.rb +3 -1
  47. data/test/committee_test.rb +3 -1
  48. data/test/drivers/hyper_schema/driver_test.rb +49 -0
  49. data/test/drivers/{hyper_schema_test.rb → hyper_schema/link_test.rb} +2 -45
  50. data/test/drivers/open_api_2/driver_test.rb +156 -0
  51. data/test/drivers/open_api_2/header_schema_builder_test.rb +26 -0
  52. data/test/drivers/open_api_2/link_test.rb +52 -0
  53. data/test/drivers/open_api_2/parameter_schema_builder_test.rb +195 -0
  54. data/test/drivers/{open_api_3_test.rb → open_api_3/driver_test.rb} +5 -3
  55. data/test/drivers_test.rb +12 -10
  56. data/test/middleware/base_test.rb +3 -1
  57. data/test/middleware/request_validation_open_api_3_test.rb +4 -2
  58. data/test/middleware/request_validation_test.rb +46 -5
  59. data/test/middleware/response_validation_open_api_3_test.rb +3 -1
  60. data/test/middleware/response_validation_test.rb +39 -4
  61. data/test/middleware/stub_test.rb +3 -1
  62. data/test/request_unpacker_test.rb +2 -2
  63. data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +2 -2
  64. data/test/schema_validator/hyper_schema/request_validator_test.rb +3 -1
  65. data/test/schema_validator/hyper_schema/response_generator_test.rb +3 -1
  66. data/test/schema_validator/hyper_schema/response_validator_test.rb +3 -1
  67. data/test/schema_validator/hyper_schema/router_test.rb +5 -3
  68. data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +3 -1
  69. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +3 -1
  70. data/test/schema_validator/open_api_3/request_validator_test.rb +11 -1
  71. data/test/schema_validator/open_api_3/response_validator_test.rb +3 -1
  72. data/test/test/methods_new_version_test.rb +3 -1
  73. data/test/test/methods_test.rb +4 -2
  74. data/test/test_helper.rb +16 -16
  75. data/test/validation_error_test.rb +3 -1
  76. metadata +52 -6
  77. data/lib/committee/schema_validator/schema_validator.rb +0 -15
  78. data/test/drivers/open_api_2_test.rb +0 -416
@@ -1,49 +1,6 @@
1
- require_relative "../test_helper"
1
+ # frozen_string_literal: true
2
2
 
3
- describe Committee::Drivers::HyperSchema do
4
- before do
5
- @driver = Committee::Drivers::HyperSchema.new
6
- end
7
-
8
- it "has a name" do
9
- assert_equal :hyper_schema, @driver.name
10
- end
11
-
12
- it "has a schema class" do
13
- assert_equal Committee::Drivers::HyperSchema::Schema, @driver.schema_class
14
- end
15
-
16
- it "parses a hyper-schema and builds routes" do
17
- schema = @driver.parse(hyper_schema_data)
18
- assert_kind_of Committee::Drivers::HyperSchema::Schema, schema
19
- assert_equal @driver, schema.driver
20
-
21
- assert_kind_of Hash, schema.routes
22
- refute schema.routes.empty?
23
- assert(schema.routes.keys.all? { |m|
24
- ["DELETE", "GET", "PATCH", "POST", "PUT"].include?(m)
25
- })
26
-
27
- schema.routes.each do |(_, method_routes)|
28
- method_routes.each do |regex, link|
29
- assert_kind_of Regexp, regex
30
- assert_kind_of Committee::Drivers::HyperSchema::Link, link
31
- end
32
- end
33
- end
34
-
35
- it "defaults to not coercing form parameters" do
36
- assert_equal false, @driver.default_coerce_form_params
37
- end
38
-
39
- it "defaults to no path parameters" do
40
- assert_equal false, @driver.default_path_params
41
- end
42
-
43
- it "defaults to no query parameters" do
44
- assert_equal false, @driver.default_query_params
45
- end
46
- end
3
+ require "test_helper"
47
4
 
48
5
  describe Committee::Drivers::HyperSchema::Link do
49
6
  before do
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Committee::Drivers::OpenAPI2::Driver do
6
+ before do
7
+ @driver = Committee::Drivers::OpenAPI2::Driver.new
8
+ end
9
+
10
+ it "has a name" do
11
+ assert_equal :open_api_2, @driver.name
12
+ end
13
+
14
+ it "has a schema class" do
15
+ assert_equal Committee::Drivers::OpenAPI2::Schema, @driver.schema_class
16
+ end
17
+
18
+ it "parses an OpenAPI 2 spec" do
19
+ schema = @driver.parse(open_api_2_data)
20
+ assert_kind_of Committee::Drivers::OpenAPI2::Schema, schema
21
+ assert_kind_of JsonSchema::Schema, schema.definitions
22
+ assert_equal @driver, schema.driver
23
+
24
+ assert_kind_of Hash, schema.routes
25
+ refute schema.routes.empty?
26
+ assert(schema.routes.keys.all? { |m|
27
+ ["DELETE", "GET", "PATCH", "POST", "PUT"].include?(m)
28
+ })
29
+
30
+ schema.routes.each do |(_, method_routes)|
31
+ method_routes.each do |regex, link|
32
+ assert_kind_of Regexp, regex
33
+ assert_kind_of Committee::Drivers::OpenAPI2::Link, link
34
+
35
+ # verify that we've correct generated a parameters schema for each link
36
+ if link.target_schema
37
+ assert_kind_of JsonSchema::Schema, link.schema if link.schema
38
+ end
39
+
40
+ if link.target_schema
41
+ assert_kind_of JsonSchema::Schema, link.target_schema
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ it "names capture groups into href regexes" do
48
+ schema = @driver.parse(open_api_2_data)
49
+ assert_equal %r{^\/api\/pets\/(?<id>[^\/]+)$}.inspect,
50
+ schema.routes["DELETE"][0][0].inspect
51
+ end
52
+
53
+ it "prefers a 200 response first" do
54
+ schema_data = schema_data_with_responses({
55
+ '201' => { 'schema' => { 'description' => '201 response' } },
56
+ '200' => { 'schema' => { 'description' => '200 response' } },
57
+ })
58
+
59
+ schema = @driver.parse(schema_data)
60
+ link = schema.routes['GET'][0][1]
61
+ assert_equal 200, link.status_success
62
+ assert_equal({ 'description' => '200 response' }, link.target_schema.data)
63
+ end
64
+
65
+ it "prefers a 201 response next" do
66
+ schema_data = schema_data_with_responses({
67
+ '302' => { 'schema' => { 'description' => '302 response' } },
68
+ '201' => { 'schema' => { 'description' => '201 response' } },
69
+ })
70
+
71
+ schema = @driver.parse(schema_data)
72
+ link = schema.routes['GET'][0][1]
73
+ assert_equal 201, link.status_success
74
+ assert_equal({ 'description' => '201 response' }, link.target_schema.data)
75
+ end
76
+
77
+ it "prefers any three-digit response next" do
78
+ schema_data = schema_data_with_responses({
79
+ 'default' => { 'schema' => { 'description' => 'default response' } },
80
+ '302' => { 'schema' => { 'description' => '302 response' } },
81
+ })
82
+
83
+ schema = @driver.parse(schema_data)
84
+ link = schema.routes['GET'][0][1]
85
+ assert_equal 302, link.status_success
86
+ assert_equal({ 'description' => '302 response' }, link.target_schema.data)
87
+ end
88
+
89
+ it "prefers any numeric three-digit response next" do
90
+ schema_data = schema_data_with_responses({
91
+ 'default' => { 'schema' => { 'description' => 'default response' } },
92
+ 302 => { 'schema' => { 'description' => '302 response' } },
93
+ })
94
+
95
+ schema = @driver.parse(schema_data)
96
+ link = schema.routes['GET'][0][1]
97
+ assert_equal 302, link.status_success
98
+ assert_equal({ 'description' => '302 response' }, link.target_schema.data)
99
+ end
100
+
101
+ it "falls back to no response" do
102
+ schema_data = schema_data_with_responses({})
103
+
104
+ schema = @driver.parse(schema_data)
105
+ link = schema.routes['GET'][0][1]
106
+ assert_nil link.status_success
107
+ assert_nil link.target_schema
108
+ end
109
+
110
+ it "refuses to parse other version of OpenAPI" do
111
+ data = open_api_2_data
112
+ data['swagger'] = '3.0'
113
+ e = assert_raises(ArgumentError) do
114
+ @driver.parse(data)
115
+ end
116
+ assert_equal "Committee: driver requires OpenAPI 2.0.", e.message
117
+ end
118
+
119
+ it "refuses to parse a spec without mandatory fields" do
120
+ data = open_api_2_data
121
+ data['definitions'] = nil
122
+ e = assert_raises(ArgumentError) do
123
+ @driver.parse(data)
124
+ end
125
+ assert_equal "Committee: no definitions section in spec data.", e.message
126
+ end
127
+
128
+ it "defaults to coercing form parameters" do
129
+ assert_equal true, @driver.default_coerce_form_params
130
+ end
131
+
132
+ it "defaults to path parameters" do
133
+ assert_equal true, @driver.default_path_params
134
+ end
135
+
136
+ it "defaults to query parameters" do
137
+ assert_equal true, @driver.default_query_params
138
+ end
139
+
140
+ def schema_data_with_responses(response_data)
141
+ {
142
+ 'swagger' => '2.0',
143
+ 'consumes' => ['application/json'],
144
+ 'produces' => ['application/json'],
145
+ 'paths' => {
146
+ '/foos' => {
147
+ 'get' => {
148
+ 'responses' => response_data,
149
+ },
150
+ },
151
+ },
152
+ 'definitions' => {},
153
+ }
154
+ end
155
+ end
156
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Committee::Drivers::OpenAPI2::HeaderSchemaBuilder do
6
+ it "returns schema data for header" do
7
+ data = {
8
+ "parameters" => [
9
+ {
10
+ "name" => "AUTH_TOKEN",
11
+ "type" => "string",
12
+ "in" => "header",
13
+ }
14
+ ]
15
+ }
16
+ schema = call(data)
17
+
18
+ assert_equal ["string"], schema.properties["AUTH_TOKEN"].type
19
+ end
20
+
21
+ private
22
+
23
+ def call(data)
24
+ Committee::Drivers::OpenAPI2::HeaderSchemaBuilder.new(data).call
25
+ end
26
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Committee::Drivers::OpenAPI2::Link do
6
+ before do
7
+ @link = Committee::Drivers::OpenAPI2::Link.new
8
+ @link.enc_type = "application/x-www-form-urlencoded"
9
+ @link.href = "/apps"
10
+ @link.media_type = "application/json"
11
+ @link.method = "GET"
12
+ @link.status_success = 200
13
+ @link.schema = { "title" => "input" }
14
+ @link.target_schema = { "title" => "target" }
15
+ end
16
+
17
+ it "uses set #enc_type" do
18
+ assert_equal "application/x-www-form-urlencoded", @link.enc_type
19
+ end
20
+
21
+ it "uses set #href" do
22
+ assert_equal "/apps", @link.href
23
+ end
24
+
25
+ it "uses set #media_type" do
26
+ assert_equal "application/json", @link.media_type
27
+ end
28
+
29
+ it "uses set #method" do
30
+ assert_equal "GET", @link.method
31
+ end
32
+
33
+ it "proxies #rel" do
34
+ e = assert_raises do
35
+ @link.rel
36
+ end
37
+ assert_equal "Committee: rel not implemented for OpenAPI", e.message
38
+ end
39
+
40
+ it "uses set #schema" do
41
+ assert_equal({ "title" => "input" }, @link.schema)
42
+ end
43
+
44
+ it "uses set #status_success" do
45
+ assert_equal 200, @link.status_success
46
+ end
47
+
48
+ it "uses set #target_schema" do
49
+ assert_equal({ "title" => "target" }, @link.target_schema)
50
+ end
51
+ end
52
+
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Committee::Drivers::OpenAPI2::ParameterSchemaBuilder do
6
+ before do
7
+ end
8
+
9
+ it "reflects a basic type into a schema" do
10
+ data = {
11
+ "parameters" => [
12
+ {
13
+ "name" => "limit",
14
+ "type" => "integer",
15
+ }
16
+ ]
17
+ }
18
+ schema, schema_data = call(data)
19
+
20
+ assert_nil schema_data
21
+ assert_equal ["limit"], schema.properties.keys
22
+ assert_equal [], schema.required
23
+ assert_equal ["integer"], schema.properties["limit"].type
24
+ assert_nil schema.properties["limit"].enum
25
+ assert_nil schema.properties["limit"].format
26
+ assert_nil schema.properties["limit"].pattern
27
+ assert_nil schema.properties["limit"].min_length
28
+ assert_nil schema.properties["limit"].max_length
29
+ assert_nil schema.properties["limit"].min_items
30
+ assert_nil schema.properties["limit"].max_items
31
+ assert_nil schema.properties["limit"].unique_items
32
+ assert_nil schema.properties["limit"].min
33
+ assert_nil schema.properties["limit"].min_exclusive
34
+ assert_nil schema.properties["limit"].max
35
+ assert_nil schema.properties["limit"].max_exclusive
36
+ assert_nil schema.properties["limit"].multiple_of
37
+ end
38
+
39
+ it "reflects a required property into a schema" do
40
+ data = {
41
+ "parameters" => [
42
+ {
43
+ "name" => "limit",
44
+ "required" => true,
45
+ }
46
+ ]
47
+ }
48
+ schema, schema_data = call(data)
49
+
50
+ assert_nil schema_data
51
+ assert_equal ["limit"], schema.required
52
+ end
53
+
54
+ it "reflects an array with an items schema into a schema" do
55
+ data = {
56
+ "parameters" => [
57
+ {
58
+ "name" => "tags",
59
+ "type" => "array",
60
+ "minItems" => 1,
61
+ "maxItems" => 10,
62
+ "uniqueItems" => true,
63
+ "items" => {
64
+ "type" => "string"
65
+ }
66
+ }
67
+ ]
68
+ }
69
+ schema, schema_data = call(data)
70
+
71
+ assert_nil schema_data
72
+ assert_equal ["array"], schema.properties["tags"].type
73
+ assert_equal 1, schema.properties["tags"].min_items
74
+ assert_equal 10, schema.properties["tags"].max_items
75
+ assert_equal true, schema.properties["tags"].unique_items
76
+ assert_equal({ "type" => "string" }, schema.properties["tags"].items)
77
+ end
78
+
79
+ it "reflects a enum property into a schema" do
80
+ data = {
81
+ "parameters" => [
82
+ {
83
+ "name" => "type",
84
+ "type" => "string",
85
+ "enum" => ["hoge", "fuga"]
86
+ }
87
+ ]
88
+ }
89
+ schema, schema_data = call(data)
90
+
91
+ assert_nil schema_data
92
+ assert_equal ["hoge", "fuga"], schema.properties["type"].enum
93
+ end
94
+
95
+ it "reflects string properties into a schema" do
96
+ data = {
97
+ "parameters" => [
98
+ {
99
+ "name" => "password",
100
+ "type" => "string",
101
+ "format" => "password",
102
+ "pattern" => "[a-zA-Z0-9]+",
103
+ "minLength" => 6,
104
+ "maxLength" => 30
105
+ }
106
+ ]
107
+ }
108
+ schema, schema_data = call(data)
109
+
110
+ assert_nil schema_data
111
+ assert_equal "password", schema.properties["password"].format
112
+ assert_equal Regexp.new("[a-zA-Z0-9]+"), schema.properties["password"].pattern
113
+ assert_equal 6, schema.properties["password"].min_length
114
+ assert_equal 30, schema.properties["password"].max_length
115
+ end
116
+
117
+ it "reflects number properties into a schema" do
118
+ data = {
119
+ "parameters" => [
120
+ {
121
+ "name" => "limit",
122
+ "type" => "integer",
123
+ "minimum" => 20,
124
+ "exclusiveMinimum" => true,
125
+ "maximum" => 100,
126
+ "exclusiveMaximum" => false,
127
+ "multipleOf" => 10
128
+ }
129
+ ]
130
+ }
131
+ schema, schema_data = call(data)
132
+
133
+ assert_nil schema_data
134
+ assert_equal 20, schema.properties["limit"].min
135
+ assert_equal true, schema.properties["limit"].min_exclusive
136
+ assert_equal 100, schema.properties["limit"].max
137
+ assert_equal false, schema.properties["limit"].max_exclusive
138
+ assert_equal 10, schema.properties["limit"].multiple_of
139
+ end
140
+
141
+ it "returns schema data for a body parameter" do
142
+ data = {
143
+ "parameters" => [
144
+ {
145
+ "name" => "payload",
146
+ "in" => "body",
147
+ "schema" => {
148
+ "$ref" => "#/definitions/foo",
149
+ }
150
+ }
151
+ ]
152
+ }
153
+ schema, schema_data = call(data)
154
+
155
+ assert_nil schema
156
+ assert_equal({ "$ref" => "#/definitions/foo" }, schema_data)
157
+ end
158
+
159
+ it "requires that certain fields are present" do
160
+ data = {
161
+ "parameters" => [
162
+ {
163
+ }
164
+ ]
165
+ }
166
+ e = assert_raises ArgumentError do
167
+ call(data)
168
+ end
169
+ assert_equal "Committee: no name section in link data.", e.message
170
+ end
171
+
172
+ it "requires that body parameters not be mixed with form parameters" do
173
+ data = {
174
+ "parameters" => [
175
+ {
176
+ "name" => "payload",
177
+ "in" => "body",
178
+ },
179
+ {
180
+ "name" => "limit",
181
+ "in" => "form",
182
+ },
183
+ ]
184
+ }
185
+ e = assert_raises ArgumentError do
186
+ call(data)
187
+ end
188
+ assert_equal "Committee: can't mix body parameter with form parameters.",
189
+ e.message
190
+ end
191
+
192
+ def call(data)
193
+ Committee::Drivers::OpenAPI2::ParameterSchemaBuilder.new(data).call
194
+ end
195
+ end