committee 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c93972246b5dbf788152e0b307564f19bf9fd2ac
4
- data.tar.gz: b56949031b0ddff186b158b4346a71743de8c607
2
+ SHA256:
3
+ metadata.gz: eac1e86868d4ca6f8b7ef27f62ec87131ba1399985f48b0e7846a5ceda6d81cc
4
+ data.tar.gz: a0e76eea124800b0d8d328657ffb30efc3c683bbfd47707ce9c148500fc79307
5
5
  SHA512:
6
- metadata.gz: 76b74624c59f7af781abe8359df8db12ded7cc697ccea6cc9c73eb03be7e07813eec6c32b0436f54c2902c5fd4e995bc3d729ebc502c43745b7c59750dbc0919
7
- data.tar.gz: e82213545cd8918152a748d6637563fc2acd589f9e48b3f76d8b73d12b8f15f80a93f6ded6706a1d8383eaadd7cb14d0661ecf35dec1dde2cc9660387c0f43e5
6
+ metadata.gz: dcaa28428558ae7b9f6ce8679e826e62bdc0640228e35fb41a0998a1ccbbab4d505bc6e23ad052b907a45be6d2003152ab9803e132907a3c68b1a2fbf7fc5292
7
+ data.tar.gz: 0a1d640b9305e12a2ac6b7436b2971a8a1568322aa1d75a25f8ec4a25aed50229c5bd5c99e317c9c243d22099c1855ea93fbf17cc0692488a35a8f1ee03f8959
@@ -13,6 +13,34 @@ module Committee
13
13
  end
14
14
  end
15
15
 
16
+ # load and build drive from JSON file
17
+ # @param [String] schema_path
18
+ # @return [Committee::Driver]
19
+ def self.load_from_json(schema_path)
20
+ json = JSON.parse(File.read(schema_path))
21
+ load_from_data(json)
22
+ end
23
+
24
+ # load and build drive from Hash object
25
+ # @param [Hash] hash
26
+ # @return [Committee::Driver]
27
+ def self.load_from_data(hash)
28
+ driver = if hash['swagger'] == '2.0'
29
+ Committee::Drivers::OpenAPI2.new
30
+ else
31
+ Committee::Drivers::HyperSchema.new
32
+ end
33
+
34
+ driver.parse(hash)
35
+ end
36
+
37
+ # load and build drive from file
38
+ # @param [String] schema_path
39
+ # @return [Committee::Driver]
40
+ def self.load_from_file(schema_path)
41
+ load_from_json(schema_path)
42
+ end
43
+
16
44
  # Driver is a base class for driver implementations.
17
45
  class Driver
18
46
  # Whether parameters that were form-encoded will be coerced by default.
@@ -7,8 +7,7 @@ module Committee::Middleware
7
7
  @params_key = options[:params_key] || "committee.params"
8
8
  @headers_key = options[:headers_key] || "committee.headers"
9
9
  @raise = options[:raise]
10
- @schema = get_schema(options[:schema] ||
11
- raise(ArgumentError, "Committee: need option `schema`"))
10
+ @schema = get_schema(options)
12
11
 
13
12
  @router = Committee::Router.new(@schema,
14
13
  prefix: options[:prefix]
@@ -36,7 +35,14 @@ module Committee::Middleware
36
35
  # input to be a string, a data hash, or a JsonSchema::Schema. In the former
37
36
  # two cases we just parse as if we were sent hyper-schema. In the latter,
38
37
  # we have the hyper-schema driver wrap it in a new Committee object.
39
- def get_schema(schema)
38
+ def get_schema(options)
39
+ schema = options[:schema]
40
+ unless schema
41
+ schema = Committee::Drivers::load_from_file(options[:schema_path]) if options[:schema_path]
42
+
43
+ raise(ArgumentError, "Committee: need option `schema` or schema_path") unless schema
44
+ end
45
+
40
46
  # These are in a separately conditional ladder so that we only show the
41
47
  # user one warning.
42
48
  if schema.is_a?(String)
@@ -62,8 +68,8 @@ module Committee::Middleware
62
68
  # Expect the type we want by now. If we don't have it, the user passed
63
69
  # something else non-standard in.
64
70
  if !schema.is_a?(Committee::Drivers::Schema)
65
- raise ArgumentError, "Committee: schema expected to be a hash or " \
66
- "an instance of Committee::Drivers::Schema."
71
+ raise ArgumentError, "Committee: schema expected to be an instance of " \
72
+ "Committee::Drivers::Schema."
67
73
  end
68
74
 
69
75
  schema
@@ -71,17 +77,17 @@ module Committee::Middleware
71
77
 
72
78
  def warn_json_schema_deprecated
73
79
  Committee.warn_deprecated("Committee: passing a JsonSchema::Schema to schema " \
74
- "option is deprecated; please send a driver object instead.")
80
+ "option is deprecated; please send an instance of Committee::Drivers::Schema instead.")
75
81
  end
76
82
 
77
83
  def warn_hash_deprecated
78
84
  Committee.warn_deprecated("Committee: passing a hash to schema " \
79
- "option is deprecated; please send a driver object instead.")
85
+ "option is deprecated; please send an instance of Committee::Drivers::Schema instead.")
80
86
  end
81
87
 
82
88
  def warn_string_deprecated
83
89
  Committee.warn_deprecated("Committee: passing a string to schema " \
84
- "option is deprecated; please send a driver object instead.")
90
+ "option is deprecated; please send an instance of Committee::Drivers::Schema instead.")
85
91
  end
86
92
  end
87
93
  end
@@ -46,6 +46,7 @@ module Committee::Middleware
46
46
  allow_query_params: @allow_query_params,
47
47
  coerce_form_params: @coerce_form_params,
48
48
  optimistic_json: @optimistic_json,
49
+ coerce_recursive: @coerce_recursive,
49
50
  schema: link ? link.schema : nil
50
51
  ).call
51
52
 
@@ -1,10 +1,18 @@
1
1
  module Committee::Middleware
2
2
  class ResponseValidation < Base
3
- attr_reader :validate_errors
3
+ attr_reader :validate_success_only
4
4
 
5
5
  def initialize(app, options = {})
6
6
  super
7
- @validate_errors = options[:validate_errors]
7
+ @validate_success_only = options.fetch(:validate_success_only, true)
8
+
9
+ unless options[:validate_errors].nil?
10
+ @validate_success_only = !options[:validate_errors]
11
+ Committee.warn_deprecated("Committee: validate_errors option is deprecated; " \
12
+ "please use validate_success_only=#{@validate_success_only}.")
13
+ end
14
+
15
+ @error_handler = options[:error_handler]
8
16
  end
9
17
 
10
18
  def handle(request)
@@ -16,21 +24,23 @@ module Committee::Middleware
16
24
  response.each do |chunk|
17
25
  full_body << chunk
18
26
  end
19
- data = JSON.parse(full_body)
20
- Committee::ResponseValidator.new(link, validate_errors: validate_errors).call(status, headers, data)
27
+ data = full_body.empty? ? {} : JSON.parse(full_body)
28
+ Committee::ResponseValidator.new(link, validate_success_only: validate_success_only).call(status, headers, data)
21
29
  end
22
30
 
23
31
  [status, headers, response]
24
32
  rescue Committee::InvalidResponse
33
+ @error_handler.call($!) if @error_handler
25
34
  raise if @raise
26
35
  @error_class.new(500, :invalid_response, $!.message).render
27
36
  rescue JSON::ParserError
37
+ @error_handler.call($!) if @error_handler
28
38
  raise Committee::InvalidResponse if @raise
29
39
  @error_class.new(500, :invalid_response, "Response wasn't valid JSON.").render
30
40
  end
31
41
 
32
42
  def validate?(status)
33
- Committee::ResponseValidator.validate?(status, validate_errors: validate_errors)
43
+ Committee::ResponseValidator.validate?(status, validate_success_only: validate_success_only)
34
44
  end
35
45
  end
36
46
  end
@@ -7,6 +7,7 @@ module Committee
7
7
  @allow_query_params = options[:allow_query_params]
8
8
  @coerce_form_params = options[:coerce_form_params]
9
9
  @optimistic_json = options[:optimistic_json]
10
+ @coerce_recursive = options[:coerce_recursive]
10
11
  @schema = options[:schema]
11
12
  end
12
13
 
@@ -31,7 +32,7 @@ module Committee
31
32
  p = @request.POST
32
33
 
33
34
  if @coerce_form_params && @schema
34
- Committee::StringParamsCoercer.new(p, @schema).call!
35
+ Committee::StringParamsCoercer.new(p, @schema, coerce_recursive: @coerce_recursive).call!
35
36
  end
36
37
 
37
38
  p
@@ -1,18 +1,18 @@
1
1
  module Committee
2
2
  class ResponseValidator
3
- attr_reader :validate_errors
3
+ attr_reader :validate_success_only
4
4
 
5
5
  def initialize(link, options = {})
6
6
  @link = link
7
- @validate_errors = options[:validate_errors]
7
+ @validate_success_only = options[:validate_success_only]
8
8
 
9
9
  @validator = JsonSchema::Validator.new(target_schema(link))
10
10
  end
11
11
 
12
12
  def self.validate?(status, options = {})
13
- validate_errors = options[:validate_errors]
13
+ validate_success_only = options[:validate_success_only]
14
14
 
15
- status != 204 and validate_errors || (200...300).include?(status)
15
+ status != 204 and !validate_success_only || (200...300).include?(status)
16
16
  end
17
17
 
18
18
  def call(status, headers, data)
@@ -41,7 +41,7 @@ module Committee
41
41
  return if data == nil
42
42
  end
43
43
 
44
- if self.class.validate?(status, validate_errors: validate_errors) && !@validator.validate(data)
44
+ if self.class.validate?(status, validate_success_only: validate_success_only) && !@validator.validate(data)
45
45
  errors = JsonSchema::SchemaError.aggregate(@validator.errors).join("\n")
46
46
  raise InvalidResponse, "Invalid response.\n\n#{errors}"
47
47
  end
@@ -16,15 +16,15 @@ module Committee
16
16
 
17
17
  def find_link(method, path)
18
18
  path = path.gsub(@prefix_regexp, "") if @prefix
19
- if method_routes = @schema.routes[method]
20
- method_routes.each do |pattern, link|
21
- if matches = pattern.match(path)
22
- hash = Hash[matches.names.zip(matches.captures)]
23
- return link, hash
24
- end
19
+ link_with_matches = (@schema.routes[method] || []).map do |pattern, link|
20
+ if matches = pattern.match(path)
21
+ # prefer path which has fewer matches (eg. `/pets/dog` than `/pets/{uuid}` for path `/pets/dog` )
22
+ [matches.captures.size, link, Hash[matches.names.zip(matches.captures)]]
23
+ else
24
+ nil
25
25
  end
26
- end
27
- nil
26
+ end.compact.sort_by(&:first).first
27
+ link_with_matches.nil? ? nil : link_with_matches.slice(1, 2)
28
28
  end
29
29
 
30
30
  def find_request_link(request)
@@ -35,25 +35,49 @@ module Committee::Test
35
35
  @committee_router ||= Committee::Router.new(@committee_schema,
36
36
  prefix: schema_url_prefix)
37
37
 
38
- link, _ = @committee_router.find_request_link(last_request)
38
+ link, _ = @committee_router.find_request_link(request_object)
39
39
  unless link
40
- response = "`#{last_request.request_method} #{last_request.path_info}` undefined in schema."
40
+ response = "`#{request_object.request_method} #{request_object.path_info}` undefined in schema."
41
41
  raise Committee::InvalidResponse.new(response)
42
42
  end
43
43
 
44
- if validate_response?(last_response.status)
45
- data = JSON.parse(last_response.body)
46
- Committee::ResponseValidator.new(link).call(last_response.status, last_response.headers, data)
44
+ status, headers, body = response_data
45
+ if validate?(status)
46
+ data = JSON.parse(body)
47
+ Committee::ResponseValidator.new(link).call(status, headers, data)
47
48
  end
48
49
  end
49
50
 
51
+ def request_object
52
+ last_request
53
+ end
54
+
55
+ def response_data
56
+ [last_response.status, last_response.headers, last_response.body]
57
+ end
58
+
50
59
  def assert_schema_content_type
51
60
  Committee.warn_deprecated("Committee: use of #assert_schema_content_type is deprecated; use #assert_schema_conform instead.")
52
61
  end
53
62
 
63
+ # we use this method 3.0 or later
64
+ def committee_options
65
+ unless defined?(@call_committee_options_deprecated)
66
+ @call_committee_options_deprecated = true
67
+ Committee.warn_deprecated("Committee: committee 3.0 require overwrite committee options so please use this method.")
68
+ end
69
+
70
+ {}
71
+ end
72
+
54
73
  # Can be overridden with a different driver name for other API definition
55
74
  # formats.
56
75
  def committee_schema
76
+ schema = committee_options[:schema]
77
+ return schema if schema
78
+
79
+ Committee.warn_deprecated("Committee: we'll remove committee_schema method in committee 3.0;" \
80
+ "please use committee_options.")
57
81
  nil
58
82
  end
59
83
 
@@ -61,14 +85,26 @@ module Committee::Test
61
85
  # easier to access as a string
62
86
  # blob
63
87
  def schema_contents
88
+ Committee.warn_deprecated("Committee: we'll remove schema_contents method in committee 3.0;" \
89
+ "please use committee_options.")
64
90
  JSON.parse(File.read(schema_path))
65
91
  end
66
92
 
67
93
  def schema_path
94
+ Committee.warn_deprecated("Committee: we'll remove schema_path method in committee 3.0;" \
95
+ "please use committee_options.")
68
96
  raise "Please override #committee_schema."
69
97
  end
70
98
 
71
99
  def schema_url_prefix
100
+ prefix = committee_options[:prefix]
101
+ return prefix if prefix
102
+
103
+ schema = committee_options[:schema]
104
+ return nil if schema # committee_options set so we don't show warn message
105
+
106
+ Committee.warn_deprecated("Committee: we'll remove schema_url_prefix method in committee 3.0;" \
107
+ "please use committee_options.")
72
108
  nil
73
109
  end
74
110
 
@@ -85,7 +121,19 @@ module Committee::Test
85
121
  end
86
122
 
87
123
  def validate_response?(status)
88
- Committee::ResponseValidator.validate?(status)
124
+ Committee.warn_deprecated("Committee: w'll remove validate_response? method in committee 3.0")
125
+
126
+ Committee::ResponseValidator.validate?(status, validate_success_only: validate_success_only)
89
127
  end
128
+
129
+ private
130
+
131
+ def validate_success_only
132
+ committee_options.fetch(:validate_success_only, true)
133
+ end
134
+
135
+ def validate?(status)
136
+ Committee::ResponseValidator.validate?(status, validate_success_only: validate_success_only)
137
+ end
90
138
  end
91
139
  end
@@ -19,6 +19,48 @@ describe Committee::Drivers do
19
19
  end
20
20
  assert_equal %{Committee: unknown driver "blueprint".}, e.message
21
21
  end
22
+
23
+ describe 'load_from_file(schema_path)' do
24
+ it 'load OpenAPI2' do
25
+ s = Committee::Drivers.load_from_file(open_api_2_schema_path)
26
+ assert_kind_of Committee::Drivers::Schema, s
27
+ assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
28
+ end
29
+
30
+ it 'load Hyper-Schema' do
31
+ s = Committee::Drivers.load_from_file(hyper_schema_schema_path)
32
+ assert_kind_of Committee::Drivers::Schema, s
33
+ assert_kind_of Committee::Drivers::HyperSchema::Schema, s
34
+ end
35
+ end
36
+
37
+ describe 'load_from_json(schema_path)' do
38
+ it 'load OpenAPI2' do
39
+ s = Committee::Drivers.load_from_json(open_api_2_schema_path)
40
+ assert_kind_of Committee::Drivers::Schema, s
41
+ assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
42
+ end
43
+
44
+ it 'load Hyper-Schema' do
45
+ s = Committee::Drivers.load_from_json(hyper_schema_schema_path)
46
+ assert_kind_of Committee::Drivers::Schema, s
47
+ assert_kind_of Committee::Drivers::HyperSchema::Schema, s
48
+ end
49
+ end
50
+
51
+ describe 'load_from_data(schema_path)' do
52
+ it 'load OpenAPI2' do
53
+ s = Committee::Drivers.load_from_data(open_api_2_data)
54
+ assert_kind_of Committee::Drivers::Schema, s
55
+ assert_kind_of Committee::Drivers::OpenAPI2::Schema, s
56
+ end
57
+
58
+ it 'load Hyper-Schema' do
59
+ s = Committee::Drivers.load_from_data(hyper_schema_data)
60
+ assert_kind_of Committee::Drivers::Schema, s
61
+ assert_kind_of Committee::Drivers::HyperSchema::Schema, s
62
+ end
63
+ end
22
64
  end
23
65
 
24
66
  describe Committee::Drivers::Driver do
@@ -65,8 +65,19 @@ describe Committee::Middleware::Base do
65
65
  e = assert_raises(ArgumentError) do
66
66
  post "/apps"
67
67
  end
68
- assert_equal "Committee: schema expected to be a hash or an instance " +
69
- "of Committee::Drivers::Schema.", e.message
68
+ assert_equal "Committee: schema expected to be an instance of Committee::Drivers::Schema.", e.message
69
+ end
70
+
71
+ describe 'initialize option' do
72
+ it "schema_path option with hyper-schema" do
73
+ b = Committee::Middleware::Base.new(nil, schema_path: hyper_schema_schema_path)
74
+ assert_kind_of Committee::Drivers::HyperSchema::Schema, b.instance_variable_get(:@schema)
75
+ end
76
+
77
+ it "schema_path option with OpenAPI2" do
78
+ b = Committee::Middleware::Base.new(nil, schema_path: open_api_2_schema_path)
79
+ assert_kind_of Committee::Drivers::OpenAPI2::Schema, b.instance_variable_get(:@schema)
80
+ end
70
81
  end
71
82
 
72
83
  private
@@ -13,6 +13,14 @@ describe Committee::Middleware::ResponseValidation do
13
13
  assert_equal 200, last_response.status
14
14
  end
15
15
 
16
+ it "doesn't call error_handler when response is valid" do
17
+ called = false
18
+ pr = ->(_e) { called = true }
19
+ @app = new_rack_app(JSON.generate([ValidApp]), {}, schema: hyper_schema, error_handler: pr)
20
+ get "/apps"
21
+ assert !called, "error_handler is called"
22
+ end
23
+
16
24
  it "detects a response invalid due to schema" do
17
25
  @app = new_rack_app("{}", {}, schema: hyper_schema)
18
26
  get "/apps"
@@ -21,7 +29,7 @@ describe Committee::Middleware::ResponseValidation do
21
29
  end
22
30
 
23
31
  it "detects a response invalid due to not being JSON" do
24
- @app = new_rack_app("", {}, schema: hyper_schema)
32
+ @app = new_rack_app("{_}", {}, schema: hyper_schema)
25
33
  get "/apps"
26
34
  assert_equal 500, last_response.status
27
35
  assert_match(/valid JSON/i, last_response.body)
@@ -33,10 +41,26 @@ describe Committee::Middleware::ResponseValidation do
33
41
  assert_equal 404, last_response.status
34
42
  end
35
43
 
44
+ it "optionally validates non-2xx invalid responses and deprecated option" do
45
+ mock(Committee).warn_deprecated.with_any_args
46
+
47
+ @app = new_rack_app("{_}", {}, app_status: 404, validate_errors: true,
48
+ schema: hyper_schema)
49
+
50
+ get "/apps"
51
+ assert_equal 500, last_response.status
52
+ assert_match(/valid JSON/i, last_response.body)
53
+ end
54
+
36
55
  it "optionally validates non-2xx invalid responses" do
37
- @app = new_rack_app("", {}, app_status: 404, validate_errors: true,
38
- schema: hyper_schema)
56
+ @app = new_rack_app("", {}, app_status: 404, validate_success_only: false, schema: hyper_schema)
57
+ get "/apps"
58
+ assert_equal 500, last_response.status
59
+ assert_match(/Invalid response/i, last_response.body)
60
+ end
39
61
 
62
+ it "optionally validates non-2xx invalid responses with invalid json" do
63
+ @app = new_rack_app("{_}", {}, app_status: 404, validate_success_only: false, schema: hyper_schema)
40
64
  get "/apps"
41
65
  assert_equal 500, last_response.status
42
66
  assert_match(/valid JSON/i, last_response.body)
@@ -48,13 +72,28 @@ describe Committee::Middleware::ResponseValidation do
48
72
  assert_equal 204, last_response.status
49
73
  end
50
74
 
75
+ it "skip validation when 4xx" do
76
+ @app = new_rack_app("[{x:y}]", {}, schema: hyper_schema, validate_success_only: true, app_status: 400)
77
+ get "/apps"
78
+ assert_equal 400, last_response.status
79
+ assert_match("[{x:y}]", last_response.body)
80
+ end
81
+
51
82
  it "rescues JSON errors" do
52
- @app = new_rack_app("[{x:y}]", {}, schema: hyper_schema)
83
+ @app = new_rack_app("[{x:y}]", {}, schema: hyper_schema, validate_success_only: false)
53
84
  get "/apps"
54
85
  assert_equal 500, last_response.status
55
86
  assert_match(/valid json/i, last_response.body)
56
87
  end
57
88
 
89
+ it "calls error_handler when it rescues JSON errors" do
90
+ called_err = nil
91
+ pr = ->(e) { called_err = e }
92
+ @app = new_rack_app("[{x:y}]", {}, schema: hyper_schema, error_handler: pr)
93
+ get "/apps"
94
+ assert_kind_of JSON::ParserError, called_err
95
+ end
96
+
58
97
  it "takes a prefix" do
59
98
  @app = new_rack_app(JSON.generate([ValidApp]), {}, prefix: "/v1",
60
99
  schema: hyper_schema)
@@ -69,6 +108,16 @@ describe Committee::Middleware::ResponseValidation do
69
108
  end
70
109
  end
71
110
 
111
+ it "calls error_handler when it rescues JSON errors" do
112
+ called_err = nil
113
+ pr = ->(e) { called_err = e }
114
+ @app = new_rack_app("[{x:y}]", {}, raise: true, schema: hyper_schema, error_handler: pr)
115
+ assert_raises(Committee::InvalidResponse) do
116
+ get "/apps"
117
+ assert_kind_of JSON::ParserError, called_err
118
+ end
119
+ end
120
+
72
121
  it "passes through a valid response for OpenAPI" do
73
122
  @app = new_rack_app(JSON.generate([ValidPet]), {},
74
123
  schema: open_api_2_schema)
@@ -77,7 +126,7 @@ describe Committee::Middleware::ResponseValidation do
77
126
  end
78
127
 
79
128
  it "detects an invalid response for OpenAPI" do
80
- @app = new_rack_app("", {}, schema: open_api_2_schema)
129
+ @app = new_rack_app("{_}", {}, schema: open_api_2_schema)
81
130
  get "/api/pets"
82
131
  assert_equal 500, last_response.status
83
132
  assert_match(/valid JSON/i, last_response.body)
@@ -113,6 +113,30 @@ describe Committee::RequestUnpacker do
113
113
  end
114
114
  end
115
115
 
116
+ it "coerces form params with coerce_form_params, coerce_recursive and a schema" do
117
+ %w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
118
+ schema = JsonSchema::Schema.new
119
+ schema.properties = { "x" => JsonSchema::Schema.new }
120
+ schema.properties["x"].type = ["object"]
121
+ schema.properties["x"].properties = { "y" => JsonSchema::Schema.new }
122
+ schema.properties["x"].properties["y"].type = ["integer"]
123
+
124
+ env = {
125
+ "CONTENT_TYPE" => content_type,
126
+ "rack.input" => StringIO.new("x[y]=1"),
127
+ }
128
+ request = Rack::Request.new(env)
129
+ params, _ = Committee::RequestUnpacker.new(
130
+ request,
131
+ allow_form_params: true,
132
+ coerce_form_params: true,
133
+ coerce_recursive: true,
134
+ schema: schema
135
+ ).call
136
+ assert_equal({ "x" => { "y" => 1 } }, params)
137
+ end
138
+ end
139
+
116
140
  it "unpacks form & query params with allow_form_params and allow_query_params" do
117
141
  %w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
118
142
  env = {
@@ -44,6 +44,7 @@ describe Committee::Router do
44
44
  it "provides named parameters" do
45
45
  link, param_matches = open_api_2_router.find_link("GET", "/api/pets/fido")
46
46
  refute_nil link
47
+ assert_equal '/api/pets/{id}', link.href
47
48
  assert_equal({ "id" => "fido" }, param_matches)
48
49
  end
49
50
 
@@ -53,6 +54,18 @@ describe Committee::Router do
53
54
  assert_equal({}, param_matches)
54
55
  end
55
56
 
57
+ it "finds a path which has fewer matches" do
58
+ link, _ = open_api_2_router.find_link("GET", "/api/pets/dog")
59
+ refute_nil link
60
+ assert_equal '/api/pets/dog', link.href
61
+ end
62
+
63
+ it "fewer match not support in HyperSchema" do
64
+ link, _ = hyper_schema_router.find_link("GET", "/apps/abc")
65
+ refute_nil link
66
+ assert_equal '/apps/{(%23%2Fdefinitions%2Fapp%2Fdefinitions%2Fname)}', link.href
67
+ end
68
+
56
69
  def hyper_schema_router(options = {})
57
70
  schema = Committee::Drivers::HyperSchema.new.parse(hyper_schema_data)
58
71
  Committee::Router.new(schema, options)
@@ -0,0 +1,76 @@
1
+ require_relative "../test_helper"
2
+
3
+ describe Committee::Test::Methods do
4
+ include Committee::Test::Methods
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ @app
9
+ end
10
+
11
+ def committee_options
12
+ @committee_options
13
+ end
14
+
15
+ before do
16
+ # This is a little icky, but the test methods will cache router and schema
17
+ # values between tests. This makes sense in real life, but is harmful for
18
+ # our purposes here in testing the module.
19
+ @committee_router = nil
20
+ @committee_schema = nil
21
+
22
+ @committee_options = {schema: hyper_schema}
23
+ end
24
+
25
+ describe "#assert_schema_conform" do
26
+ it "passes through a valid response" do
27
+ @app = new_rack_app(JSON.generate([ValidApp]))
28
+ get "/apps"
29
+ assert_schema_conform
30
+ end
31
+
32
+ it "passes with prefix" do
33
+ @committee_options.merge!(prefix: "/v1")
34
+
35
+ @app = new_rack_app(JSON.generate([ValidApp]))
36
+ get "/v1/apps"
37
+ assert_schema_conform
38
+ end
39
+
40
+ it "detects an invalid response Content-Type" do
41
+ @app = new_rack_app(JSON.generate([ValidApp]), 200, {})
42
+ get "/apps"
43
+ e = assert_raises(Committee::InvalidResponse) do
44
+ assert_schema_conform
45
+ end
46
+ assert_match(/response header must be set to/i, e.message)
47
+ end
48
+
49
+ it "detects an invalid response Content-Type but ignore because it's not success status code" do
50
+ @app = new_rack_app(JSON.generate([ValidApp]), 400, {})
51
+ get "/apps"
52
+ assert_schema_conform
53
+ end
54
+
55
+ it "detects an invalid response Content-Type and check all status code" do
56
+ @committee_options.merge!(validate_success_only: false)
57
+
58
+ @app = new_rack_app(JSON.generate([ValidApp]), 400, {})
59
+ get "/apps"
60
+ e = assert_raises(Committee::InvalidResponse) do
61
+ assert_schema_conform
62
+ end
63
+ assert_match(/response header must be set to/i, e.message)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def new_rack_app(response, status=200, headers={ "Content-Type" => "application/json" })
70
+ Rack::Builder.new {
71
+ run lambda { |_|
72
+ [status, headers, [response]]
73
+ }
74
+ }
75
+ end
76
+ end
@@ -29,12 +29,16 @@ describe Committee::Test::Methods do
29
29
 
30
30
  describe "#assert_schema_conform" do
31
31
  it "passes through a valid response" do
32
+ mock(Committee).warn_deprecated.with_any_args.times(2)
33
+
32
34
  @app = new_rack_app(JSON.generate([ValidApp]))
33
35
  get "/apps"
34
36
  assert_schema_conform
35
37
  end
36
38
 
37
39
  it "detects an invalid response Content-Type" do
40
+ mock(Committee).warn_deprecated.with_any_args.times(2)
41
+
38
42
  @app = new_rack_app(JSON.generate([ValidApp]), {})
39
43
  get "/apps"
40
44
  e = assert_raises(Committee::InvalidResponse) do
@@ -44,7 +48,7 @@ describe Committee::Test::Methods do
44
48
  end
45
49
 
46
50
  it "accepts schema string (legacy behavior)" do
47
- mock(Committee).warn_deprecated.with_any_args
51
+ mock(Committee).warn_deprecated.with_any_args.times(3)
48
52
 
49
53
  stub(self).committee_schema { nil }
50
54
  stub(self).schema_contents { JSON.dump(hyper_schema_data) }
@@ -55,7 +59,7 @@ describe Committee::Test::Methods do
55
59
  end
56
60
 
57
61
  it "accepts schema hash (legacy behavior)" do
58
- mock(Committee).warn_deprecated.with_any_args
62
+ mock(Committee).warn_deprecated.with_any_args.times(3)
59
63
 
60
64
  stub(self).committee_schema { nil }
61
65
  stub(self).schema_contents { hyper_schema_data }
@@ -69,6 +73,7 @@ describe Committee::Test::Methods do
69
73
  # Note we don't warn here because this is a recent deprecation and
70
74
  # passing a schema object will not be a huge performance hit. We should
71
75
  # probably start warning on the next version.
76
+ mock(Committee).warn_deprecated.with_any_args.times(2)
72
77
 
73
78
  stub(self).committee_schema { nil }
74
79
  stub(self).schema_contents do
@@ -47,25 +47,27 @@ ValidPet = {
47
47
  }.freeze
48
48
 
49
49
  def hyper_schema
50
- @hyper_schema ||= begin
51
- driver = Committee::Drivers::HyperSchema.new
52
- driver.parse(hyper_schema_data)
53
- end
50
+ @hyper_schema ||= Committee::Drivers.load_from_file(hyper_schema_schema_path)
54
51
  end
55
52
 
56
53
  def open_api_2_schema
57
- @open_api_2_schema ||= begin
58
- driver = Committee::Drivers::OpenAPI2.new
59
- driver.parse(open_api_2_data)
60
- end
54
+ @open_api_2_schema ||= Committee::Drivers.load_from_data(open_api_2_data)
61
55
  end
62
56
 
63
57
  # Don't cache this because we'll often manipulate the created hash in tests.
64
58
  def hyper_schema_data
65
- JSON.parse(File.read("./test/data/hyperschema/paas.json"))
59
+ JSON.parse(File.read(hyper_schema_schema_path))
66
60
  end
67
61
 
68
62
  # Don't cache this because we'll often manipulate the created hash in tests.
69
63
  def open_api_2_data
70
- JSON.parse(File.read("./test/data/openapi2/petstore-expanded.json"))
64
+ JSON.parse(File.read(open_api_2_schema_path))
65
+ end
66
+
67
+ def hyper_schema_schema_path
68
+ "./test/data/hyperschema/paas.json"
69
+ end
70
+
71
+ def open_api_2_schema_path
72
+ "./test/data/openapi2/petstore-expanded.json"
71
73
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: committee
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandur
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-11-15 00:00:00.000000000 Z
12
+ date: 2019-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json_schema
@@ -189,6 +189,7 @@ files:
189
189
  - test/response_validator_test.rb
190
190
  - test/router_test.rb
191
191
  - test/string_params_coercer_test.rb
192
+ - test/test/methods_new_version_test.rb
192
193
  - test/test/methods_test.rb
193
194
  - test/test_helper.rb
194
195
  - test/validation_error_test.rb
@@ -212,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
213
  version: '0'
213
214
  requirements: []
214
215
  rubyforge_project:
215
- rubygems_version: 2.6.14
216
+ rubygems_version: 2.7.3
216
217
  signing_key:
217
218
  specification_version: 4
218
219
  summary: A collection of Rack middleware to support JSON Schema.