request_handler 2.2.0 → 3.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +8 -2
- data/.rubocop.yml +13 -14
- data/.rubocop_todo.yml +105 -0
- data/CHANGELOG.md +10 -1
- data/Gemfile +1 -10
- data/Guardfile +4 -4
- data/Rakefile +7 -6
- data/bin/console +3 -3
- data/lib/request_handler/base.rb +42 -38
- data/lib/request_handler/body_parser.rb +5 -4
- data/lib/request_handler/builder/base.rb +1 -1
- data/lib/request_handler/builder/body_builder.rb +1 -1
- data/lib/request_handler/builder/fieldsets_builder.rb +2 -2
- data/lib/request_handler/builder/fieldsets_resource_builder.rb +1 -1
- data/lib/request_handler/builder/filter_builder.rb +1 -1
- data/lib/request_handler/builder/headers_builder.rb +1 -1
- data/lib/request_handler/builder/include_options_builder.rb +1 -1
- data/lib/request_handler/builder/multipart_builder.rb +2 -2
- data/lib/request_handler/builder/multipart_resource_builder.rb +1 -1
- data/lib/request_handler/builder/options_builder.rb +12 -10
- data/lib/request_handler/builder/page_builder.rb +2 -2
- data/lib/request_handler/builder/page_resource_builder.rb +1 -1
- data/lib/request_handler/builder/query_builder.rb +1 -1
- data/lib/request_handler/builder/sort_options_builder.rb +1 -1
- data/lib/request_handler/concerns/config_helper.rb +2 -2
- data/lib/request_handler/config.rb +3 -3
- data/lib/request_handler/document_parser.rb +6 -6
- data/lib/request_handler/error.rb +17 -1
- data/lib/request_handler/fieldsets_parser.rb +28 -24
- data/lib/request_handler/filter_parser.rb +15 -13
- data/lib/request_handler/header_parser.rb +10 -9
- data/lib/request_handler/include_option_parser.rb +18 -17
- data/lib/request_handler/json_api_document_parser.rb +31 -15
- data/lib/request_handler/json_parser.rb +4 -3
- data/lib/request_handler/multipart_parser.rb +20 -15
- data/lib/request_handler/option_parser.rb +3 -3
- data/lib/request_handler/page_parser.rb +33 -25
- data/lib/request_handler/query_parser.rb +6 -6
- data/lib/request_handler/schema_parser.rb +20 -17
- data/lib/request_handler/sort_option_parser.rb +19 -15
- data/lib/request_handler/validation/definition_engine.rb +8 -6
- data/lib/request_handler/validation/dry_engine.rb +9 -7
- data/lib/request_handler/validation/engine.rb +4 -3
- data/lib/request_handler/validation/errors.rb +2 -0
- data/lib/request_handler/validation/result.rb +1 -0
- data/lib/request_handler/version.rb +1 -1
- data/lib/request_handler.rb +8 -8
- data/request_handler.gemspec +42 -36
- metadata +179 -52
- data/lib/request_handler/base_parser.rb +0 -9
@@ -4,7 +4,7 @@ module RequestHandler
|
|
4
4
|
module Concerns
|
5
5
|
module ConfigHelper
|
6
6
|
def lookup!(hash, key)
|
7
|
-
lookup(hash, key) || (raise NoConfigAvailableError
|
7
|
+
lookup(hash, key) || (raise NoConfigAvailableError.new(key.to_sym => "is not configured"))
|
8
8
|
end
|
9
9
|
|
10
10
|
def lookup(config, key)
|
@@ -12,7 +12,7 @@ module RequestHandler
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def symbolize_key(key)
|
15
|
-
key.split(
|
15
|
+
key.split(".").map(&:to_sym)
|
16
16
|
end
|
17
17
|
|
18
18
|
def deep_to_h(obj)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "docile"
|
4
4
|
|
5
5
|
module RequestHandler
|
6
6
|
class Config
|
@@ -11,7 +11,7 @@ module RequestHandler
|
|
11
11
|
attr_accessor :config
|
12
12
|
|
13
13
|
def lookup!(key)
|
14
|
-
lookup(key) || (raise NoConfigAvailableError
|
14
|
+
lookup(key) || (raise NoConfigAvailableError.new(key.to_sym => "is not configured"))
|
15
15
|
end
|
16
16
|
|
17
17
|
def lookup(key)
|
@@ -21,7 +21,7 @@ module RequestHandler
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def symbolize_key(key)
|
24
|
-
key.split(
|
24
|
+
key.split(".").map(&:to_sym)
|
25
25
|
end
|
26
26
|
|
27
27
|
def deep_to_h(obj)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/json_api_document_parser"
|
4
|
+
require "request_handler/json_parser"
|
5
5
|
|
6
6
|
module RequestHandler
|
7
7
|
module DocumentParser
|
@@ -12,13 +12,13 @@ module RequestHandler
|
|
12
12
|
type = type.to_sym unless type.nil?
|
13
13
|
PARSER_MAPPING
|
14
14
|
.fetch(type) { raise InternalArgumentError.new(detail: "parser for type '#{type}' not found") }
|
15
|
-
.new(args)
|
15
|
+
.new(**args)
|
16
16
|
end
|
17
17
|
|
18
18
|
PARSER_MAPPING = {
|
19
|
-
nil
|
20
|
-
:jsonapi
|
21
|
-
:json
|
19
|
+
nil => JsonApiDocumentParser, # no config defaults to jsonapi
|
20
|
+
:jsonapi => JsonApiDocumentParser,
|
21
|
+
:json => JsonParser
|
22
22
|
}.freeze
|
23
23
|
end
|
24
24
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module RequestHandler
|
4
4
|
class BaseError < StandardError
|
5
5
|
attr_reader :errors
|
6
|
+
|
6
7
|
def initialize(errors)
|
7
8
|
@errors = errors
|
8
9
|
super(message)
|
@@ -11,13 +12,16 @@ module RequestHandler
|
|
11
12
|
def message
|
12
13
|
errors.map do |key, value|
|
13
14
|
"#{key}: #{value}"
|
14
|
-
end.join(
|
15
|
+
end.join(", ")
|
15
16
|
end
|
16
17
|
end
|
18
|
+
|
17
19
|
class InternalBaseError < BaseError
|
18
20
|
end
|
21
|
+
|
19
22
|
class ExternalBaseError < BaseError
|
20
23
|
end
|
24
|
+
|
21
25
|
class JsonApiError < ExternalBaseError
|
22
26
|
def message
|
23
27
|
@errors.map do |error|
|
@@ -29,31 +33,43 @@ module RequestHandler
|
|
29
33
|
RequestHandler.configuration.raise_jsonapi_errors ? @errors : []
|
30
34
|
end
|
31
35
|
end
|
36
|
+
|
32
37
|
class MissingArgumentError < InternalBaseError
|
33
38
|
end
|
39
|
+
|
34
40
|
class ExternalArgumentError < JsonApiError
|
35
41
|
end
|
42
|
+
|
36
43
|
class InternalArgumentError < InternalBaseError
|
37
44
|
end
|
45
|
+
|
38
46
|
class SchemaValidationError < JsonApiError
|
39
47
|
end
|
48
|
+
|
40
49
|
class OptionNotAllowedError < JsonApiError
|
41
50
|
end
|
51
|
+
|
42
52
|
class NoConfigAvailableError < InternalBaseError
|
43
53
|
end
|
44
54
|
|
45
55
|
class BodyParamsError < ExternalArgumentError
|
46
56
|
end
|
57
|
+
|
47
58
|
class FieldsetsParamsError < ExternalArgumentError
|
48
59
|
end
|
60
|
+
|
49
61
|
class FilterParamsError < ExternalArgumentError
|
50
62
|
end
|
63
|
+
|
51
64
|
class IncludeParamsError < ExternalArgumentError
|
52
65
|
end
|
66
|
+
|
53
67
|
class PageParamsError < ExternalArgumentError
|
54
68
|
end
|
69
|
+
|
55
70
|
class SortParamsError < ExternalArgumentError
|
56
71
|
end
|
72
|
+
|
57
73
|
class MultipartParamsError < ExternalArgumentError
|
58
74
|
end
|
59
75
|
end
|
@@ -1,24 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/schema_parser"
|
4
|
+
require "request_handler/error"
|
5
5
|
module RequestHandler
|
6
6
|
class FieldsetsParser
|
7
7
|
def initialize(params:, allowed: {}, required: [])
|
8
8
|
@params = params
|
9
9
|
allowed.reject! { |_k, v| v == false }
|
10
10
|
allowed.each_pair do |_key, value|
|
11
|
-
raise InternalArgumentError
|
11
|
+
raise InternalArgumentError.new(allowed: "must be a Schema or a Boolean") unless
|
12
12
|
RequestHandler.configuration.validation_engine.valid_schema?(value) ||
|
13
|
-
|
13
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
14
14
|
end
|
15
15
|
@allowed = allowed
|
16
|
-
raise InternalArgumentError
|
16
|
+
raise InternalArgumentError.new(required: "must be an Array") unless required.is_a?(Array)
|
17
|
+
|
17
18
|
@required = required
|
18
19
|
end
|
19
20
|
|
20
21
|
def run
|
21
|
-
fields = params[
|
22
|
+
fields = params["fields"]
|
22
23
|
raise_missing_fields_param unless fields
|
23
24
|
fieldsets = fields.to_h.each_with_object({}) do |(type, values), memo|
|
24
25
|
type = type.to_sym
|
@@ -31,7 +32,7 @@ module RequestHandler
|
|
31
32
|
private
|
32
33
|
|
33
34
|
def parse_options(type, values)
|
34
|
-
values.split(
|
35
|
+
values.split(",").map! do |option|
|
35
36
|
parse_option(type, option)
|
36
37
|
end
|
37
38
|
end
|
@@ -43,45 +44,48 @@ module RequestHandler
|
|
43
44
|
RequestHandler.configuration.validation_engine.validate!(option, allowed[type]).output.to_sym
|
44
45
|
end
|
45
46
|
rescue Validation::Error
|
46
|
-
raise FieldsetsParamsError
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
raise FieldsetsParamsError.new([{ code: "INVALID_QUERY_PARAMETER",
|
48
|
+
status: "400",
|
49
|
+
detail: "allowed fieldset does not include '#{option}'",
|
50
|
+
source: { parameter: "fields[#{type}]" } }])
|
50
51
|
end
|
51
52
|
|
52
53
|
def check_required_fieldsets_types(fieldsets)
|
53
54
|
missing = required - fieldsets.keys
|
54
55
|
return fieldsets if missing.empty?
|
56
|
+
|
55
57
|
raise_missing_fieldsets!(missing)
|
56
58
|
end
|
57
59
|
|
58
60
|
def raise_invalid_field_option(type)
|
59
|
-
return if allowed
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
return if allowed[type]
|
62
|
+
|
63
|
+
raise OptionNotAllowedError.new([
|
64
|
+
{
|
65
|
+
code: "INVALID_QUERY_PARAMETER",
|
66
|
+
status: "400",
|
67
|
+
detail: "fieldset for '#{type}' not allowed",
|
68
|
+
source: { parameter: "fields[#{type}]" }
|
69
|
+
}
|
70
|
+
])
|
68
71
|
end
|
69
72
|
|
70
73
|
def raise_missing_fields_param
|
71
74
|
return if required.empty?
|
75
|
+
|
72
76
|
raise_missing_fieldsets!(required)
|
73
77
|
end
|
74
78
|
|
75
79
|
def raise_missing_fieldsets!(missing)
|
76
80
|
errors = missing.map do |type|
|
77
81
|
{
|
78
|
-
code:
|
79
|
-
status:
|
80
|
-
source: { parameter:
|
82
|
+
code: "MISSING_QUERY_PARAMETER",
|
83
|
+
status: "400",
|
84
|
+
source: { parameter: "" },
|
81
85
|
detail: "missing required parameter fields[#{type}]"
|
82
86
|
}
|
83
87
|
end
|
84
|
-
raise FieldsetsParamsError
|
88
|
+
raise FieldsetsParamsError.new(errors)
|
85
89
|
end
|
86
90
|
|
87
91
|
attr_reader :params, :allowed, :required
|
@@ -1,51 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/schema_parser"
|
4
|
+
require "request_handler/error"
|
5
5
|
module RequestHandler
|
6
6
|
class FilterParser < SchemaParser
|
7
7
|
def initialize(params:, schema:, additional_url_filter:, schema_options: {})
|
8
8
|
super(schema: schema, schema_options: schema_options)
|
9
|
-
@filter = params.fetch(
|
10
|
-
raise FilterParamsError
|
9
|
+
@filter = params.fetch("filter") { {} }
|
10
|
+
raise FilterParamsError.new([jsonapi_filter_syntax_error]) unless @filter.is_a?(Hash)
|
11
|
+
|
11
12
|
Array(additional_url_filter).each do |key|
|
12
13
|
key = key.to_s
|
13
14
|
raise build_error(key) unless @filter[key].nil?
|
14
|
-
|
15
|
+
|
16
|
+
@filter[key] = params.fetch(key, nil)
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
def run
|
19
21
|
validate_schema(filter)
|
20
22
|
rescue SchemaValidationError => e
|
21
|
-
raise FilterParamsError
|
23
|
+
raise FilterParamsError.new((e.errors.map do |schema_error|
|
22
24
|
source_param = "filter[#{schema_error[:source][:pointer]}]"
|
23
25
|
{
|
24
26
|
detail: schema_error[:detail],
|
25
27
|
**jsonapi_filter_base_error(source_param: source_param)
|
26
28
|
}
|
27
|
-
end)
|
29
|
+
end))
|
28
30
|
end
|
29
31
|
|
30
32
|
private
|
31
33
|
|
32
34
|
def build_error(_key)
|
33
|
-
InternalArgumentError.new(filter:
|
35
|
+
InternalArgumentError.new(filter: "the filter key was set twice")
|
34
36
|
end
|
35
37
|
|
36
38
|
def jsonapi_filter_base_error(source_param:)
|
37
39
|
{
|
38
|
-
status:
|
39
|
-
code:
|
40
|
+
status: "400",
|
41
|
+
code: "INVALID_QUERY_PARAMETER",
|
40
42
|
source: { parameter: source_param }
|
41
43
|
}
|
42
44
|
end
|
43
45
|
|
44
46
|
def jsonapi_filter_syntax_error
|
45
47
|
{
|
46
|
-
**jsonapi_filter_base_error(source_param:
|
47
|
-
links:
|
48
|
-
detail:
|
48
|
+
**jsonapi_filter_base_error(source_param: "filter"),
|
49
|
+
links: { about: "https://jsonapi.org/recommendations/#filtering" },
|
50
|
+
detail: "Filter parameter must conform to JSON API recommendation"
|
49
51
|
}
|
50
52
|
end
|
51
53
|
|
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/schema_parser"
|
4
|
+
require "request_handler/error"
|
5
5
|
|
6
6
|
module RequestHandler
|
7
7
|
class HeaderParser < SchemaParser
|
8
8
|
def initialize(env:, schema: nil, schema_options: {})
|
9
9
|
super(schema: schema, schema_options: schema_options) unless schema.nil?
|
10
10
|
|
11
|
-
raise MissingArgumentError
|
12
|
-
|
13
|
-
|
11
|
+
raise MissingArgumentError.new(env: "is missing") if env.nil?
|
12
|
+
|
13
|
+
@headers = Helper.deep_transform_keys_in_object(env.select { |k, _v| k.start_with?("HTTP_") }) do |k|
|
14
|
+
k[5..].downcase.to_sym
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
@@ -25,22 +26,22 @@ module RequestHandler
|
|
25
26
|
def validate_headers!
|
26
27
|
validate_schema(headers)
|
27
28
|
rescue SchemaValidationError => e
|
28
|
-
raise ExternalArgumentError
|
29
|
+
raise ExternalArgumentError.new(external_argument_error_params(e))
|
29
30
|
end
|
30
31
|
|
31
32
|
def external_argument_error_params(error)
|
32
33
|
error.errors.map do |schema_error|
|
33
34
|
header = schema_error[:source][:pointer]
|
34
35
|
{
|
35
|
-
status:
|
36
|
-
code:
|
36
|
+
status: "400",
|
37
|
+
code: "#{headers[header.to_sym] ? 'INVALID' : 'MISSING'}_HEADER",
|
37
38
|
detail: "#{format_header_name(header)} #{schema_error[:detail]}"
|
38
39
|
}
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
43
|
def format_header_name(name)
|
43
|
-
name.split(
|
44
|
+
name.split("_").map(&:capitalize).join("-")
|
44
45
|
end
|
45
46
|
|
46
47
|
attr_reader :headers
|
@@ -1,43 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/option_parser"
|
4
|
+
require "request_handler/error"
|
5
5
|
module RequestHandler
|
6
6
|
class IncludeOptionParser < OptionParser
|
7
7
|
def run
|
8
|
-
return [] unless params.key?(
|
8
|
+
return [] unless params.key?("include")
|
9
|
+
|
9
10
|
options = fetch_options
|
10
|
-
raise_error(
|
11
|
-
allowed_options(options.split(
|
11
|
+
raise_error("INVALID_QUERY_PARAMETER", "must not contain a space") if options.include?(" ")
|
12
|
+
allowed_options(options.split(","))
|
12
13
|
end
|
13
14
|
|
14
15
|
def allowed_options(options)
|
15
16
|
options.map do |option|
|
16
|
-
option.gsub!(
|
17
|
+
option.gsub!(".", ::RequestHandler.configuration.separator)
|
17
18
|
begin
|
18
19
|
RequestHandler.configuration.validation_engine.validate!(option, allowed_options_type).output.to_sym
|
19
20
|
rescue Validation::Error
|
20
|
-
raise_error(
|
21
|
+
raise_error("OPTION_NOT_ALLOWED", "#{option} is not an allowed include option", OptionNotAllowedError)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
26
|
def fetch_options
|
26
|
-
raise_error(
|
27
|
-
params.fetch(
|
27
|
+
raise_error("INVALID_QUERY_PARAMETER", "must not be empty") if empty_param?("include")
|
28
|
+
params.fetch("include", "")
|
28
29
|
end
|
29
30
|
|
30
31
|
private
|
31
32
|
|
32
33
|
def raise_error(code, detail, error_klass = IncludeParamsError)
|
33
|
-
raise error_klass
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
raise error_klass.new([
|
35
|
+
{
|
36
|
+
status: "400",
|
37
|
+
code: code,
|
38
|
+
detail: detail,
|
39
|
+
source: { parameter: "include" }
|
40
|
+
}
|
41
|
+
])
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
@@ -1,13 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/schema_parser"
|
4
|
+
require "request_handler/error"
|
5
5
|
module RequestHandler
|
6
6
|
class JsonApiDocumentParser < SchemaParser
|
7
7
|
NON_ATTRIBUTE_MEMBERS = %i[id type meta links].freeze
|
8
|
+
# SEE: https://jsonapi.org/format/1.0/#document-resource-objects
|
9
|
+
VALID_DATA_MEMBERS = %w[id type attributes relationships links meta].freeze
|
8
10
|
|
9
11
|
def initialize(document:, schema:, schema_options: {})
|
10
|
-
raise MissingArgumentError
|
12
|
+
raise MissingArgumentError.new(data: "is missing") if document.nil?
|
13
|
+
|
11
14
|
super(schema: schema, schema_options: schema_options)
|
12
15
|
@document = document
|
13
16
|
end
|
@@ -20,25 +23,38 @@ module RequestHandler
|
|
20
23
|
private
|
21
24
|
|
22
25
|
def flattened_document
|
23
|
-
resource = document.fetch(
|
24
|
-
raise BodyParamsError
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
resource = document.fetch("data") do
|
27
|
+
raise BodyParamsError.new([{ code: "INVALID_JSON_API",
|
28
|
+
status: "400",
|
29
|
+
title: "Body is not a valid JSON API payload",
|
30
|
+
detail: "Member 'data' is missing",
|
31
|
+
source: { pointer: "/" } }])
|
29
32
|
end
|
33
|
+
validate_json_api_data_members(resource)
|
30
34
|
flatten_resource!(resource)
|
31
35
|
end
|
32
36
|
|
37
|
+
def validate_json_api_data_members(data)
|
38
|
+
data.each_key do |k|
|
39
|
+
next if VALID_DATA_MEMBERS.include?(k)
|
40
|
+
|
41
|
+
raise BodyParamsError.new([{ code: "INVALID_JSON_API",
|
42
|
+
status: "400",
|
43
|
+
title: "Body is not a valid JSON API payload",
|
44
|
+
detail: "Member 'data' contains invalid member!",
|
45
|
+
source: { pointer: "/data/#{k}" } }])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
33
49
|
def flatten_resource!(resource)
|
34
|
-
resource.merge!(resource.delete(
|
35
|
-
relationships = flatten_relationship_resource_linkages(resource.delete(
|
50
|
+
resource.merge!(resource.delete("attributes") { {} })
|
51
|
+
relationships = flatten_relationship_resource_linkages(resource.delete("relationships") { {} })
|
36
52
|
resource.merge!(relationships)
|
37
53
|
end
|
38
54
|
|
39
55
|
def flatten_relationship_resource_linkages(relationships)
|
40
56
|
relationships.each_with_object({}) do |(k, v), memo|
|
41
|
-
resource_linkage = v[
|
57
|
+
resource_linkage = v["data"]
|
42
58
|
memo[k] = resource_linkage
|
43
59
|
end
|
44
60
|
end
|
@@ -46,9 +62,9 @@ module RequestHandler
|
|
46
62
|
def build_pointer(error)
|
47
63
|
non_nested_identifier = error[:schema_pointer] == error[:element].to_s
|
48
64
|
non_attribute_member = NON_ATTRIBUTE_MEMBERS.include?(error[:element])
|
49
|
-
[
|
50
|
-
(
|
51
|
-
error[:schema_pointer]].compact.join(
|
65
|
+
["/data",
|
66
|
+
("attributes" unless non_attribute_member && non_nested_identifier),
|
67
|
+
error[:schema_pointer]].compact.join("/")
|
52
68
|
end
|
53
69
|
|
54
70
|
attr_reader :document
|
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "request_handler/schema_parser"
|
4
|
+
require "request_handler/error"
|
5
5
|
module RequestHandler
|
6
6
|
class JsonParser < SchemaParser
|
7
7
|
def initialize(document:, schema:, schema_options: {})
|
8
|
-
raise MissingArgumentError
|
8
|
+
raise MissingArgumentError.new(json: "no content sent in document") if document.nil?
|
9
|
+
|
9
10
|
super(schema: schema, schema_options: schema_options)
|
10
11
|
@document = document
|
11
12
|
end
|
@@ -1,23 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "request_handler/concerns/config_helper"
|
4
|
+
require "request_handler/document_parser"
|
5
|
+
require "request_handler/error"
|
6
|
+
require "request_handler/schema_parser"
|
7
7
|
|
8
8
|
module RequestHandler
|
9
|
-
class MultipartsParser
|
9
|
+
class MultipartsParser
|
10
|
+
include RequestHandler::Concerns::ConfigHelper
|
11
|
+
|
10
12
|
def initialize(request:, multipart_config:)
|
11
13
|
@request = request
|
12
14
|
@params = request.params
|
13
15
|
@multipart_config = multipart_config
|
14
|
-
raise MissingArgumentError
|
16
|
+
raise MissingArgumentError.new([{ multipart_config: "is missing" }]) if multipart_config.nil?
|
15
17
|
end
|
16
18
|
|
17
19
|
def run
|
18
20
|
deep_to_h(multipart_config).each_with_object({}) do |(name, config), indexed_parts|
|
19
21
|
validate_presence!(name) if config[:required]
|
20
22
|
next if params[name.to_s].nil?
|
23
|
+
|
21
24
|
indexed_parts[name] = parse_part(name.to_s)
|
22
25
|
end
|
23
26
|
end
|
@@ -26,19 +29,20 @@ module RequestHandler
|
|
26
29
|
|
27
30
|
def validate_presence!(sidecar_name)
|
28
31
|
return if params.key?(sidecar_name.to_s)
|
32
|
+
|
29
33
|
raise multipart_params_error("missing required sidecar resource: #{sidecar_name}")
|
30
34
|
end
|
31
35
|
|
32
|
-
def multipart_params_error(detail =
|
36
|
+
def multipart_params_error(detail = "")
|
33
37
|
MultipartParamsError.new([{
|
34
|
-
status:
|
35
|
-
code:
|
38
|
+
status: "400",
|
39
|
+
code: "INVALID_MULTIPART_REQUEST",
|
36
40
|
detail: detail
|
37
41
|
}])
|
38
42
|
end
|
39
43
|
|
40
44
|
def parse_part(name)
|
41
|
-
params[name].fetch(:tempfile) { raise MultipartParamsError
|
45
|
+
params[name].fetch(:tempfile) { raise MultipartParamsError.new([{ multipart_file: "missing" }]) }
|
42
46
|
if lookup(multipart_config, "#{name}.schema")
|
43
47
|
parse_data(name)
|
44
48
|
else
|
@@ -50,10 +54,10 @@ module RequestHandler
|
|
50
54
|
data = load_json(name)
|
51
55
|
type = lookup(multipart_config, "#{name}.type")
|
52
56
|
DocumentParser.new(
|
53
|
-
type:
|
54
|
-
document:
|
55
|
-
schema:
|
56
|
-
schema_options:
|
57
|
+
type: type,
|
58
|
+
document: data,
|
59
|
+
schema: lookup(multipart_config, "#{name}.schema"),
|
60
|
+
schema_options: execute_options(lookup(multipart_config, "#{name}.options"))
|
57
61
|
).run
|
58
62
|
end
|
59
63
|
|
@@ -63,7 +67,7 @@ module RequestHandler
|
|
63
67
|
file = file.read
|
64
68
|
MultiJson.load(file)
|
65
69
|
rescue MultiJson::ParseError
|
66
|
-
raise multipart_params_error(
|
70
|
+
raise multipart_params_error("sidecar resource is not valid JSON")
|
67
71
|
end
|
68
72
|
|
69
73
|
def multipart_file(name)
|
@@ -73,6 +77,7 @@ module RequestHandler
|
|
73
77
|
def execute_options(options)
|
74
78
|
return {} if options.nil?
|
75
79
|
return options unless options.respond_to?(:call)
|
80
|
+
|
76
81
|
options.call(self, request)
|
77
82
|
end
|
78
83
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "request_handler/error"
|
4
4
|
module RequestHandler
|
5
5
|
class OptionParser
|
6
6
|
def initialize(params:, allowed_options_type:)
|
7
7
|
@params = params
|
8
8
|
@allowed_options_type = allowed_options_type
|
9
|
-
raise InternalArgumentError
|
9
|
+
raise InternalArgumentError.new(allowed_options_type: "must be a Schema") unless schema?
|
10
10
|
end
|
11
11
|
|
12
12
|
private
|
@@ -16,7 +16,7 @@ module RequestHandler
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def empty_param?(param)
|
19
|
-
params.fetch(param
|
19
|
+
params.fetch(param, nil) == ""
|
20
20
|
end
|
21
21
|
attr_reader :params, :allowed_options_type
|
22
22
|
end
|