request_handler 1.2.0 → 1.3.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
2
  SHA256:
3
- metadata.gz: fb27be2f216ddde5e61c75588e2fcd838ec4d252919fac9761372f63d1187c80
4
- data.tar.gz: dbac14a26b2de68aa1d7cf506622f849bc77a2b949424db1ffa33d70a9bbf76c
3
+ metadata.gz: fbf5f683f749e9a5431fe1d07a250c43e72446ddc4fdbb472acd7b06d737fad6
4
+ data.tar.gz: e12067caf9c5ec19b306a00d89666fac1a862449f8a68d17cb3ccda42e4a0cde
5
5
  SHA512:
6
- metadata.gz: fac2d974663982067de6d60c16e47e63bca678925f29b6d4ccbc4e25857929ca85f7da1512c61215bd01b502c145846d738c6aa3590b21f395e39eadd6f119d6
7
- data.tar.gz: 7656a364baaa0ccc75a179682541e0fe3052972b4272f1f6ced7dedf7ba06e925cbc0936f41d9cadff6b984666e04add629ea96f0d282c25e31bc871090f57d9
6
+ metadata.gz: df12c3277ca4cb9d73bd8a1661427db90b257bc263e10f40344775a4e3f6f7c8f558a5873cfacab95d888cd7d3ea6e835b72ebc2aa9c176117196530e5a585a2
7
+ data.tar.gz: a53b49ccdfde6d9fb929263729972bc5732f01e2997848299ce1dbec187be23a565b5a745ca00dc6cd5765b20bff62ccc2b729abd80bb8ad3965f1ce457e4808
@@ -0,0 +1,75 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+
6
+ common_steps: &common_steps
7
+ - checkout
8
+ - run: gem update --system
9
+ - restore_cache:
10
+ key: gem-cache-{{ .Branch }}-{{ checksum "request_handler.gemspec" }}
11
+ - run:
12
+ name: install dependencies
13
+ command: |
14
+ gem update bundler
15
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
16
+ - save_cache:
17
+ key: gem-cache-{{ .Branch }}-{{ checksum "request_handler.gemspec" }}
18
+ paths:
19
+ - vendor/bundle
20
+
21
+ - run: ruby -v
22
+ - run: bundle exec danger
23
+ - run:
24
+ name: run tests
25
+ command: |
26
+ mkdir /tmp/test-results
27
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
28
+ circleci tests split --split-by=timings)"
29
+
30
+ bundle exec rspec \
31
+ --format progress \
32
+ --format RspecJunitFormatter \
33
+ --out /tmp/test-results/rspec.xml \
34
+ --format progress \
35
+ $TEST_FILES
36
+ # collect reports
37
+ - store_test_results:
38
+ path: /tmp/test-results
39
+ - store_artifacts:
40
+ path: /tmp/test-results
41
+ destination: test-results
42
+
43
+ version: 2
44
+ jobs:
45
+ ruby-2.4:
46
+ docker:
47
+ - image: circleci/ruby:2.4
48
+ steps:
49
+ *common_steps
50
+ ruby-2.5:
51
+ docker:
52
+ - image: circleci/ruby:2.5
53
+ steps:
54
+ *common_steps
55
+ jruby-9.2:
56
+ docker:
57
+ - image: circleci/jruby:9.2
58
+ steps:
59
+ *common_steps
60
+ jruby-9.2-indy:
61
+ docker:
62
+ - image: circleci/jruby:9.2
63
+ environment:
64
+ JRUBY_OPTS: '-Xcompile.invokedynamic=true'
65
+ steps:
66
+ *common_steps
67
+
68
+ workflows:
69
+ version: 2
70
+ build:
71
+ jobs:
72
+ - ruby-2.4
73
+ - ruby-2.5
74
+ - jruby-9.2
75
+ - jruby-9.2-indy
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.3.0] - 2019-05-21
8
+ ### Added
9
+ - Serializable JSONAPI error objects out of validation failures
10
+ - Configuration option to enable returning validation failures in errors method of exceptions
11
+
12
+ ### Changed
13
+ - Format of error messages for external causes (bad requests)
14
+ - Inheritance chain of error classes
15
+
7
16
  ## [1.2.0] - 2019-04-15
8
17
  ### Added
9
18
  - Apart from dry-validation, now definition or any other validation library can be used
data/Gemfile CHANGED
@@ -9,4 +9,5 @@ group :development, :test do
9
9
  gem 'danger'
10
10
  gem 'danger-commit_lint'
11
11
  gem 'danger-rubocop'
12
+ gem 'rspec_junit_formatter'
12
13
  end
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # RequestHandler
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/request_handler.svg)](https://badge.fury.io/rb/request_handler)
4
- [![Build Status](https://travis-ci.org/runtastic/request_handler.svg?branch=master)](https://travis-ci.org/runtastic/request_handler)
4
+ [![CircleCI](https://circleci.com/gh/andreaseger/receptacle.svg?style=svg)](https://circleci.com/gh/runtastic/request_handler)
5
5
  [![codecov](https://codecov.io/gh/runtastic/request_handler/branch/master/graph/badge.svg)](https://codecov.io/gh/runtastic/request_handler)
6
6
 
7
7
  This gem allows easy and dry handling of requests based on the dry-validation
@@ -36,6 +36,15 @@ RequestHandler.configure do
36
36
  end
37
37
  ```
38
38
 
39
+ JSON:API-style error data can be included in validation errors raised by `RequestHandler`.
40
+
41
+ ```ruby
42
+ RequestHandler.configure do
43
+ raise_jsonapi_errors = true # default: false
44
+ end
45
+ ```
46
+
47
+
39
48
  ### Validation Engine
40
49
  Per default this gem uses the `DryEngine` which relies on dry-validation. All
41
50
  examples in this Readme assume you are using this default engine. However
@@ -256,6 +265,44 @@ and an additional file `image.png`, the resulting `multipart_params` will be the
256
265
 
257
266
  Please note that each part's content has to be uploaded as a separate file currently.
258
267
 
268
+ ### JSON:API errors
269
+
270
+ Errors caused by bad requests respond to `:errors`.
271
+
272
+ When the gem is configured to `raise_jsonapi_errors`, this method returns a list of hashes
273
+ containing `code`, `status`, `detail`, (`links`) and `source` for each specific issue
274
+ that contributed to the error. Otherwise it returns an empty array.
275
+
276
+ The exception message contains `<error code>: <source> <detail>` for every issue,
277
+ with one issue per line.
278
+
279
+ | `:code` | `:status` | What is it? |
280
+ |:--------------------------|:----------|:------------|
281
+ | INVALID_RESOURCE_SCHEMA | 422 | Resource did not pass configured validation |
282
+ | INVALID_QUERY_PARAMETER | 400 | Query parameter violates syntax or did not pass configured validation |
283
+ | MISSING_QUERY_PARAMETER | 400 | Query parameter required in configuration is missing |
284
+ | INVALID_JSON_API | 400 | Request body violates JSON:API syntax |
285
+ | INVALID_MULTIPART_REQUEST | 400 | Sidecar resource missing or invalid JSON |
286
+
287
+ #### Example
288
+ ```ruby
289
+ rescue RequestHandler::SchemaValidationError => e
290
+ puts e.errors
291
+ end
292
+ ```
293
+
294
+ ```ruby
295
+ [
296
+ {
297
+ status: '422',
298
+ code: 'INVALID_RESOURCE_SCHEMA',
299
+ title: 'Invalid resource',
300
+ detail: 'is missing',
301
+ source: { pointer: '/data/attributes/name' }
302
+ }
303
+ ]
304
+ ```
305
+
259
306
  ### Caveats
260
307
 
261
308
  It is currently expected that _url_ parameter are already parsed and included in
@@ -19,6 +19,7 @@ module RequestHandler
19
19
  logger Logger.new(STDOUT)
20
20
  separator '__'
21
21
  validation_engine Validation::DryEngine
22
+ raise_jsonapi_errors false
22
23
  end
23
24
  end
24
25
 
@@ -160,7 +160,7 @@ module RequestHandler
160
160
 
161
161
  def params
162
162
  raise MissingArgumentError, params: 'is missing' if request.params.nil?
163
- raise ExternalArgumentError, params: 'must be a Hash' unless request.params.is_a?(Hash)
163
+ raise ExternalArgumentError, [] unless request.params.is_a?(Hash)
164
164
  @params ||= Helper.deep_transform_keys_in_object(request.params) do |k|
165
165
  k.to_s.gsub('.', ::RequestHandler.separator)
166
166
  end
@@ -18,15 +18,26 @@ module RequestHandler
18
18
  end
19
19
  class ExternalBaseError < BaseError
20
20
  end
21
+ class JsonApiError < ExternalBaseError
22
+ def message
23
+ @errors.map do |error|
24
+ "#{error[:code]}: #{error[:source]} #{error[:detail]}"
25
+ end.join(',\n')
26
+ end
27
+
28
+ def errors
29
+ RequestHandler.configuration.raise_jsonapi_errors ? @errors : []
30
+ end
31
+ end
21
32
  class MissingArgumentError < InternalBaseError
22
33
  end
23
- class ExternalArgumentError < ExternalBaseError
34
+ class ExternalArgumentError < JsonApiError
24
35
  end
25
36
  class InternalArgumentError < InternalBaseError
26
37
  end
27
- class SchemaValidationError < ExternalBaseError
38
+ class SchemaValidationError < JsonApiError
28
39
  end
29
- class OptionNotAllowedError < ExternalBaseError
40
+ class OptionNotAllowedError < JsonApiError
30
41
  end
31
42
  class NoConfigAvailableError < InternalBaseError
32
43
  end
@@ -42,22 +42,45 @@ module RequestHandler
42
42
  RequestHandler.engine.validate!(option, allowed[type]).output.to_sym
43
43
  end
44
44
  rescue Validation::Error
45
- raise FieldsetsParamsError, fieldsets: "invalid field: <#{option}> for type: #{type}"
45
+ raise FieldsetsParamsError, [{ code: 'INVALID_QUERY_PARAMETER',
46
+ status: '400',
47
+ detail: "allowed fieldset does not include '#{option}'",
48
+ source: { param: "fields[#{type}]" } }]
46
49
  end
47
50
 
48
51
  def check_required_fieldsets_types(fieldsets)
49
- return fieldsets if (required - fieldsets.keys).empty?
50
- raise FieldsetsParamsError, fieldsets: 'missing required fieldsets parameter'
52
+ missing = required - fieldsets.keys
53
+ return fieldsets if missing.empty?
54
+ raise_missing_fieldsets!(missing)
51
55
  end
52
56
 
53
57
  def raise_invalid_field_option(type)
54
58
  return if allowed.key?(type)
55
- raise OptionNotAllowedError, fieldsets: "fieldsets for type: #{type} not allowed"
59
+ raise OptionNotAllowedError, [
60
+ {
61
+ code: 'INVALID_QUERY_PARAMETER',
62
+ status: '400',
63
+ detail: "fieldset for '#{type}' not allowed",
64
+ source: { param: "fields[#{type}]" }
65
+ }
66
+ ]
56
67
  end
57
68
 
58
69
  def raise_missing_fields_param
59
70
  return if required.empty?
60
- raise FieldsetsParamsError, fieldsets: 'missing required fields options'
71
+ raise_missing_fieldsets!(required)
72
+ end
73
+
74
+ def raise_missing_fieldsets!(missing)
75
+ errors = missing.map do |type|
76
+ {
77
+ code: 'MISSING_QUERY_PARAMETER',
78
+ status: '400',
79
+ source: { param: '' },
80
+ detail: "missing required parameter fields[#{type}]"
81
+ }
82
+ end
83
+ raise FieldsetsParamsError, errors
61
84
  end
62
85
 
63
86
  attr_reader :params, :allowed, :required
@@ -7,7 +7,7 @@ module RequestHandler
7
7
  def initialize(params:, schema:, additional_url_filter:, schema_options: {})
8
8
  super(schema: schema, schema_options: schema_options)
9
9
  @filter = params.fetch('filter') { {} }
10
- raise FilterParamsError, filter: 'must be a Hash' unless @filter.is_a?(Hash)
10
+ raise FilterParamsError, [jsonapi_filter_syntax_error] unless @filter.is_a?(Hash)
11
11
  Array(additional_url_filter).each do |key|
12
12
  key = key.to_s
13
13
  raise build_error(key) unless @filter[key].nil?
@@ -17,6 +17,14 @@ module RequestHandler
17
17
 
18
18
  def run
19
19
  validate_schema(filter)
20
+ rescue SchemaValidationError => e
21
+ raise FilterParamsError, (e.errors.map do |schema_error|
22
+ source_param = "filter[#{schema_error[:source][:pointer]}]"
23
+ {
24
+ detail: schema_error[:detail],
25
+ **jsonapi_filter_base_error(source_param: source_param)
26
+ }
27
+ end)
20
28
  end
21
29
 
22
30
  private
@@ -25,6 +33,22 @@ module RequestHandler
25
33
  InternalArgumentError.new(filter: 'the filter key was set twice')
26
34
  end
27
35
 
36
+ def jsonapi_filter_base_error(source_param:)
37
+ {
38
+ status: '400',
39
+ code: 'INVALID_QUERY_PARAMETER',
40
+ source: { param: source_param }
41
+ }
42
+ end
43
+
44
+ def jsonapi_filter_syntax_error
45
+ {
46
+ **jsonapi_filter_base_error(source_param: 'filter'),
47
+ links: { about: 'https://jsonapi.org/recommendations/#filtering' },
48
+ detail: 'Filter parameter must conform to JSON API recommendation'
49
+ }
50
+ end
51
+
28
52
  attr_reader :filter
29
53
  end
30
54
  end
@@ -7,7 +7,7 @@ module RequestHandler
7
7
  def run
8
8
  return [] unless params.key?('include')
9
9
  options = fetch_options
10
- raise IncludeParamsError, include: 'must not contain a space' if options.include? ' '
10
+ raise_error('INVALID_QUERY_PARAMETER', 'must not contain a space') if options.include?(' ')
11
11
  allowed_options(options.split(','))
12
12
  end
13
13
 
@@ -17,14 +17,27 @@ module RequestHandler
17
17
  begin
18
18
  RequestHandler.engine.validate!(option, allowed_options_type).output.to_sym
19
19
  rescue Validation::Error
20
- raise OptionNotAllowedError, option.to_sym => 'is not an allowed include option'
20
+ raise_error('OPTION_NOT_ALLOWED', "#{option} is not an allowed include option", OptionNotAllowedError)
21
21
  end
22
22
  end
23
23
  end
24
24
 
25
25
  def fetch_options
26
- raise IncludeParamsError, include_options: 'query paramter must not be empty' if empty_param?('include')
26
+ raise_error('INVALID_QUERY_PARAMETER', 'must not be empty') if empty_param?('include')
27
27
  params.fetch('include') { '' }
28
28
  end
29
+
30
+ private
31
+
32
+ def raise_error(code, detail, error_klass = IncludeParamsError)
33
+ raise error_klass, [
34
+ {
35
+ status: '400',
36
+ code: code,
37
+ detail: detail,
38
+ source: { param: 'include' }
39
+ }
40
+ ]
41
+ end
29
42
  end
30
43
  end
@@ -4,6 +4,8 @@ require 'request_handler/schema_parser'
4
4
  require 'request_handler/error'
5
5
  module RequestHandler
6
6
  class JsonApiDocumentParser < SchemaParser
7
+ NON_ATTRIBUTE_MEMBERS = %i[id type meta links].freeze
8
+
7
9
  def initialize(document:, schema:, schema_options: {})
8
10
  raise MissingArgumentError, "data": 'is missing' if document.nil?
9
11
  super(schema: schema, schema_options: schema_options)
@@ -19,7 +21,11 @@ module RequestHandler
19
21
 
20
22
  def flattened_document
21
23
  resource = document.fetch('data') do
22
- raise BodyParamsError, resource: 'must contain data'
24
+ raise BodyParamsError, [{ code: 'INVALID_JSON_API',
25
+ status: '400',
26
+ title: 'Body is not valid JSON API payload',
27
+ detail: "Member 'data' is missing",
28
+ source: { pointer: '/' } }]
23
29
  end
24
30
  flatten_resource!(resource)
25
31
  end
@@ -37,6 +43,14 @@ module RequestHandler
37
43
  end
38
44
  end
39
45
 
46
+ def build_pointer(error)
47
+ non_nested_identifier = error[:schema_pointer] == error[:element].to_s
48
+ non_attribute_member = NON_ATTRIBUTE_MEMBERS.include?(error[:element])
49
+ ['/data',
50
+ ('attributes' unless non_attribute_member && non_nested_identifier),
51
+ error[:schema_pointer]].compact.join('/')
52
+ end
53
+
40
54
  attr_reader :document
41
55
  end
42
56
  end
@@ -10,14 +10,12 @@ module RequestHandler
10
10
  @request = request
11
11
  @params = request.params
12
12
  @multipart_config = multipart_config
13
- missing_arguments = []
14
- missing_arguments << { multipart_config: 'is missing' } if multipart_config.nil?
15
- raise MissingArgumentError, missing_arguments unless missing_arguments.empty?
13
+ raise MissingArgumentError, [{ multipart_config: 'is missing' }] if multipart_config.nil?
16
14
  end
17
15
 
18
16
  def run
19
17
  multipart_config.each_with_object({}) do |(name, config), memo|
20
- raise MultipartParamsError, multipart: "#{name} missing" if config[:required] && !params.key?(name.to_s)
18
+ validate_presence!(name) if config[:required]
21
19
  next if params[name.to_s].nil?
22
20
  memo[name] = parse_part(name.to_s)
23
21
  end
@@ -25,8 +23,21 @@ module RequestHandler
25
23
 
26
24
  private
27
25
 
26
+ def validate_presence!(sidecar_name)
27
+ return if params.key?(sidecar_name.to_s)
28
+ raise multipart_params_error("missing required sidecar resource: #{sidecar_name}")
29
+ end
30
+
31
+ def multipart_params_error(detail = '')
32
+ MultipartParamsError.new([{
33
+ status: '400',
34
+ code: 'INVALID_MULTIPART_REQUEST',
35
+ detail: detail
36
+ }])
37
+ end
38
+
28
39
  def parse_part(name)
29
- params[name].fetch(:tempfile) { raise MultipartParamsError, multipart_file: 'missing' }
40
+ params[name].fetch(:tempfile) { raise MultipartParamsError, [{ multipart_file: 'missing' }] }
30
41
  if lookup("#{name}.schema")
31
42
  parse_data(name)
32
43
  else
@@ -51,7 +62,7 @@ module RequestHandler
51
62
  file = file.read
52
63
  MultiJson.load(file)
53
64
  rescue MultiJson::ParseError
54
- raise MultipartParamsError, multipart_file: 'invalid JSON'
65
+ raise multipart_params_error('sidecar resource is not valid JSON')
55
66
  end
56
67
 
57
68
  def multipart_file(name)
@@ -38,8 +38,7 @@ module RequestHandler
38
38
 
39
39
  def extract_number(prefix: nil)
40
40
  number_string = lookup_nested_params_key('number', prefix) || 1
41
- error_msg = { :"#{prefix}#{::RequestHandler.separator}number" => 'must be a positive Integer' }
42
- check_int(string: number_string, error_msg: error_msg)
41
+ check_int(string: number_string, param: "#{prefix}.number")
43
42
  end
44
43
 
45
44
  def extract_size(prefix: nil)
@@ -59,16 +58,24 @@ module RequestHandler
59
58
  def fetch_and_check_size(prefix)
60
59
  size_string = lookup_nested_params_key('size', prefix)
61
60
  return nil if size_string.nil?
62
- error_msg = { :"#{prefix}#{::RequestHandler.separator}size" => 'must be a positive Integer' }
63
- check_int(string: size_string, error_msg: error_msg) unless size_string.nil?
61
+ check_int(string: size_string, param: "#{prefix}.size") unless size_string.nil?
64
62
  end
65
63
 
66
- def check_int(string:, error_msg:)
64
+ def check_int(string:, param:)
67
65
  output = Integer(string)
68
- raise PageParamsError, error_msg unless output > 0
66
+ raise_page_param_error!(param) unless output > 0
69
67
  output
70
68
  rescue ArgumentError
71
- raise PageParamsError, error_msg
69
+ raise_page_param_error!(param)
70
+ end
71
+
72
+ def raise_page_param_error!(param)
73
+ raise PageParamsError, [{
74
+ code: 'INVALID_QUERY_PARAMETER',
75
+ status: '400',
76
+ detail: 'must be a positive integer',
77
+ source: { param: "page[#{param}]" }
78
+ }]
72
79
  end
73
80
 
74
81
  def apply_max_size_constraint(size, prefix)
@@ -14,6 +14,14 @@ module RequestHandler
14
14
 
15
15
  def run
16
16
  validate_schema(query)
17
+ rescue SchemaValidationError => e
18
+ raise ExternalArgumentError, (e.errors.map do |schema_error|
19
+ param = schema_error[:source][:pointer]
20
+ { status: '400',
21
+ code: "#{query[param] ? 'INVALID' : 'MISSING'}_QUERY_PARAMETER",
22
+ detail: schema_error[:detail],
23
+ source: { param: param } }
24
+ end)
17
25
  end
18
26
 
19
27
  private
@@ -28,12 +28,46 @@ module RequestHandler
28
28
 
29
29
  def validation_failure?(validator)
30
30
  return if validator.valid?
31
- errors = validator.errors.each_with_object({}) do |(k, v), memo|
32
- add_note(v, k, memo)
31
+
32
+ errors = build_errors(validator.errors).map do |error|
33
+ jsonapi_error(error)
33
34
  end
34
35
  raise SchemaValidationError, errors
35
36
  end
36
37
 
38
+ def build_errors(error_hash, path = [])
39
+ errors = []
40
+ error_hash.each do |k, v|
41
+ errors += build_errors(v, path << k).flatten if v.is_a?(Hash)
42
+ v.each { |error| errors << error(path, k, error) } if v.is_a?(Array)
43
+ errors << error(path, k, v) if v.is_a?(String)
44
+ end
45
+ errors
46
+ end
47
+
48
+ def error(path, element, failure)
49
+ schema_pointer = RequestHandler.engine.error_pointer(failure) || (path + [element]).join('/')
50
+ {
51
+ schema_pointer: schema_pointer,
52
+ element: element,
53
+ message: RequestHandler.engine.error_message(failure)
54
+ }
55
+ end
56
+
57
+ def jsonapi_error(error)
58
+ {
59
+ status: '422',
60
+ code: 'INVALID_RESOURCE_SCHEMA',
61
+ title: 'Invalid resource',
62
+ detail: error[:message],
63
+ source: { pointer: build_pointer(error) }
64
+ }
65
+ end
66
+
67
+ def build_pointer(error)
68
+ error[:schema_pointer]
69
+ end
70
+
37
71
  def add_note(v, k, memo)
38
72
  memo[k] = if v.is_a? Array
39
73
  v.join(' ')
@@ -8,12 +8,12 @@ module RequestHandler
8
8
  def run
9
9
  return [] unless params.key?('sort')
10
10
  sort_options = parse_options(fetch_options)
11
- raise SortParamsError, sort_options: 'must be unique' if duplicates?(sort_options)
11
+ raise SortParamsError, [jsonapi_error('sort options must be unique')] if duplicates?(sort_options)
12
12
  sort_options
13
13
  end
14
14
 
15
15
  def fetch_options
16
- raise SortParamsError, sort_options: 'the query paramter must not be empty' if empty_param?('sort')
16
+ raise SortParamsError, [jsonapi_error('must not be empty')] if empty_param?('sort')
17
17
  params.fetch('sort') { '' }.split(',')
18
18
  end
19
19
 
@@ -27,7 +27,7 @@ module RequestHandler
27
27
  end
28
28
 
29
29
  def parse_option(option)
30
- raise SortParamsError, sort_options: 'must not contain a space' if option.include? ' '
30
+ raise SortParamsError, [jsonapi_error('must not contain spaces')] if option.include? ' '
31
31
  if option.start_with?('-')
32
32
  [option[1..-1], :desc]
33
33
  else
@@ -38,11 +38,22 @@ module RequestHandler
38
38
  def allowed_option(name)
39
39
  RequestHandler.engine.validate!(name, allowed_options_type).output
40
40
  rescue Validation::Error
41
- raise OptionNotAllowedError, name.to_sym => 'is not an allowed sort option'
41
+ raise OptionNotAllowedError, [jsonapi_error("#{name} is not an allowed sort option")]
42
42
  end
43
43
 
44
44
  def duplicates?(options)
45
45
  !options.uniq!(&:field).nil?
46
46
  end
47
+
48
+ private
49
+
50
+ def jsonapi_error(detail)
51
+ {
52
+ code: 'INVALID_QUERY_PARAMETER',
53
+ status: '400',
54
+ source: { param: 'sort' },
55
+ detail: detail
56
+ }
57
+ end
47
58
  end
48
59
  end
@@ -21,6 +21,14 @@ module RequestHandler
21
21
  raise Validation::Error unless result.valid?
22
22
  end
23
23
  end
24
+
25
+ def self.error_message(validation_error)
26
+ validation_error.message
27
+ end
28
+
29
+ def self.error_pointer(validation_error)
30
+ validation_error.error_path.join('/')
31
+ end
24
32
  end
25
33
  end
26
34
  end
@@ -37,6 +37,14 @@ module RequestHandler
37
37
  Result.new(output: result, errors: {})
38
38
  end
39
39
  end
40
+
41
+ def self.error_message(validation_error)
42
+ validation_error
43
+ end
44
+
45
+ def self.error_pointer(_validation_error)
46
+ nil
47
+ end
40
48
  end
41
49
  end
42
50
  end
@@ -16,6 +16,14 @@ module RequestHandler
16
16
  raise NotImplementedError
17
17
  end
18
18
 
19
+ def self.error_message(_validation_error)
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def self.error_pointer(_validation_error)
24
+ raise NotImplementedError
25
+ end
26
+
19
27
  private
20
28
 
21
29
  attr_accessor :value, :schema, :options
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RequestHandler
4
- VERSION = '1.2.0'.freeze
4
+ VERSION = '1.3.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: request_handler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Eger
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-04-15 00:00:00.000000000 Z
12
+ date: 2019-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dry-validation
@@ -283,10 +283,10 @@ executables: []
283
283
  extensions: []
284
284
  extra_rdoc_files: []
285
285
  files:
286
+ - ".circleci/config.yml"
286
287
  - ".gitignore"
287
288
  - ".rspec"
288
289
  - ".rubocop.yml"
289
- - ".travis.yml"
290
290
  - CHANGELOG.md
291
291
  - CODE_OF_CONDUCT.md
292
292
  - Dangerfile
@@ -344,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
344
  version: '0'
345
345
  requirements: []
346
346
  rubyforge_project:
347
- rubygems_version: 2.7.6
347
+ rubygems_version: 2.7.6.2
348
348
  signing_key:
349
349
  specification_version: 4
350
350
  summary: shared base for request_handler using dry-* gems
data/.travis.yml DELETED
@@ -1,33 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
- rvm:
4
- - 2.4.4
5
- - 2.5.3
6
- - jruby-9.2.0.0
7
- - jruby-9.2.7.0
8
- jdk:
9
- - oraclejdk8
10
- env:
11
- - "JRUBY_OPTS='--dev --debug'"
12
- - "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
13
- matrix:
14
- exclude:
15
- - rvm: 2.4.4
16
- jdk: oraclejdk8
17
- env: "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
18
- - rvm: 2.5.3
19
- jdk: oraclejdk8
20
- env: "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
21
- allow_failures:
22
- - rvm: jruby-9.2.0.0
23
- jdk: oraclejdk8
24
- env: "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
25
- - rvm: jruby-9.2.7.0
26
- jdk: oraclejdk8
27
- env: "JRUBY_OPTS='-Xcompile.invokedynamic=true --debug'"
28
- - rvm: 2.6.2
29
- before_install:
30
- - gem update --system
31
- - gem install bundler -v 2.0.1
32
- before_script:
33
- - bundle exec danger