committee 3.1.0 → 3.1.1

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.
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