easy-jsonapi 1.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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/publish-gem.yml +60 -0
  3. data/.github/workflows/rake.yml +35 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +5 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +5 -0
  9. data/Gemfile.lock +106 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +209 -0
  12. data/Rakefile +20 -0
  13. data/UsingTheRequestObject.md +74 -0
  14. data/UsingUserConfigurations.md +95 -0
  15. data/bin/bundle +114 -0
  16. data/bin/console +15 -0
  17. data/bin/htmldiff +29 -0
  18. data/bin/kramdown +29 -0
  19. data/bin/ldiff +29 -0
  20. data/bin/license_finder +29 -0
  21. data/bin/license_finder_pip.py +29 -0
  22. data/bin/maruku +29 -0
  23. data/bin/marutex +29 -0
  24. data/bin/nokogiri +29 -0
  25. data/bin/racc +29 -0
  26. data/bin/rackup +29 -0
  27. data/bin/rake +29 -0
  28. data/bin/redcarpet +29 -0
  29. data/bin/reverse_markdown +29 -0
  30. data/bin/rspec +29 -0
  31. data/bin/rubocop +29 -0
  32. data/bin/ruby-parse +29 -0
  33. data/bin/ruby-rewrite +29 -0
  34. data/bin/setup +8 -0
  35. data/bin/solargraph +29 -0
  36. data/bin/thor +29 -0
  37. data/bin/tilt +29 -0
  38. data/bin/yard +29 -0
  39. data/bin/yardoc +29 -0
  40. data/bin/yri +29 -0
  41. data/easy-jsonapi.gemspec +39 -0
  42. data/lib/easy/jsonapi.rb +12 -0
  43. data/lib/easy/jsonapi/collection.rb +144 -0
  44. data/lib/easy/jsonapi/config_manager.rb +144 -0
  45. data/lib/easy/jsonapi/config_manager/config.rb +49 -0
  46. data/lib/easy/jsonapi/document.rb +71 -0
  47. data/lib/easy/jsonapi/document/error.rb +48 -0
  48. data/lib/easy/jsonapi/document/error/error_member.rb +15 -0
  49. data/lib/easy/jsonapi/document/jsonapi.rb +26 -0
  50. data/lib/easy/jsonapi/document/jsonapi/jsonapi_member.rb +15 -0
  51. data/lib/easy/jsonapi/document/links.rb +36 -0
  52. data/lib/easy/jsonapi/document/links/link.rb +15 -0
  53. data/lib/easy/jsonapi/document/meta.rb +26 -0
  54. data/lib/easy/jsonapi/document/meta/meta_member.rb +14 -0
  55. data/lib/easy/jsonapi/document/resource.rb +56 -0
  56. data/lib/easy/jsonapi/document/resource/attributes.rb +37 -0
  57. data/lib/easy/jsonapi/document/resource/attributes/attribute.rb +29 -0
  58. data/lib/easy/jsonapi/document/resource/relationships.rb +40 -0
  59. data/lib/easy/jsonapi/document/resource/relationships/relationship.rb +50 -0
  60. data/lib/easy/jsonapi/document/resource_id.rb +28 -0
  61. data/lib/easy/jsonapi/exceptions.rb +27 -0
  62. data/lib/easy/jsonapi/exceptions/document_exceptions.rb +619 -0
  63. data/lib/easy/jsonapi/exceptions/headers_exceptions.rb +156 -0
  64. data/lib/easy/jsonapi/exceptions/naming_exceptions.rb +36 -0
  65. data/lib/easy/jsonapi/exceptions/query_params_exceptions.rb +67 -0
  66. data/lib/easy/jsonapi/exceptions/user_defined_exceptions.rb +253 -0
  67. data/lib/easy/jsonapi/field.rb +43 -0
  68. data/lib/easy/jsonapi/header_collection.rb +38 -0
  69. data/lib/easy/jsonapi/header_collection/header.rb +11 -0
  70. data/lib/easy/jsonapi/item.rb +88 -0
  71. data/lib/easy/jsonapi/middleware.rb +158 -0
  72. data/lib/easy/jsonapi/name_value_pair.rb +72 -0
  73. data/lib/easy/jsonapi/name_value_pair_collection.rb +78 -0
  74. data/lib/easy/jsonapi/parser.rb +38 -0
  75. data/lib/easy/jsonapi/parser/document_parser.rb +196 -0
  76. data/lib/easy/jsonapi/parser/headers_parser.rb +33 -0
  77. data/lib/easy/jsonapi/parser/rack_req_params_parser.rb +117 -0
  78. data/lib/easy/jsonapi/request.rb +40 -0
  79. data/lib/easy/jsonapi/request/query_param_collection.rb +56 -0
  80. data/lib/easy/jsonapi/request/query_param_collection/fields_param.rb +32 -0
  81. data/lib/easy/jsonapi/request/query_param_collection/fields_param/fieldset.rb +34 -0
  82. data/lib/easy/jsonapi/request/query_param_collection/filter_param.rb +28 -0
  83. data/lib/easy/jsonapi/request/query_param_collection/filter_param/filter.rb +34 -0
  84. data/lib/easy/jsonapi/request/query_param_collection/include_param.rb +119 -0
  85. data/lib/easy/jsonapi/request/query_param_collection/page_param.rb +55 -0
  86. data/lib/easy/jsonapi/request/query_param_collection/query_param.rb +47 -0
  87. data/lib/easy/jsonapi/request/query_param_collection/sort_param.rb +25 -0
  88. data/lib/easy/jsonapi/response.rb +22 -0
  89. data/lib/easy/jsonapi/utility.rb +158 -0
  90. data/lib/easy/jsonapi/version.rb +8 -0
  91. metadata +248 -0
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/exceptions/user_defined_exceptions'
4
+ require 'easy/jsonapi/parser/headers_parser'
5
+ require 'easy/jsonapi/utility'
6
+
7
+ module JSONAPI
8
+ module Exceptions
9
+ # Validates that Headers comply with the JSONAPI specification
10
+ module HeadersExceptions
11
+
12
+ # Media types that are complient with the spec if no parameters are included
13
+ JSONAPI_MEDIA_TYPES = ['application/vnd.api+json', '*/*', 'application/*'].freeze
14
+
15
+ # A more specific standard error to raise when an exception is found
16
+ class InvalidHeader < StandardError
17
+ attr_accessor :status_code
18
+
19
+ # Init w a status code, so that it can be accessed when rescuing an exception
20
+ def initialize(status_code)
21
+ @status_code = status_code
22
+ super
23
+ end
24
+ end
25
+
26
+ # Check http verb vs included headers
27
+ # @param env [Hash] The rack environment variable
28
+ def self.check_request(env, config_manager = nil, opts = {})
29
+ check_compliance(env, config_manager, opts)
30
+ check_http_method_against_headers(env)
31
+ end
32
+
33
+ # Check jsonapi compliance
34
+ # @param (see #check_request)
35
+ def self.check_compliance(env, config_manager = nil, opts = {})
36
+ check_content_type(env)
37
+ check_accept(env)
38
+
39
+ hdrs = JSONAPI::Parser::HeadersParser.parse(env)
40
+ usr_opts = { http_method: opts[:http_method], path: opts[:path] }
41
+ err_msg = JSONAPI::Exceptions::UserDefinedExceptions.check_user_header_requirements(hdrs, config_manager, usr_opts)
42
+ return err_msg unless err_msg.nil?
43
+ end
44
+
45
+ class << self
46
+ private
47
+
48
+ # Checks the content type of the request to see if it is jsonapi.
49
+ # @param (see #compliant?)
50
+ # @return nil Returns nil if no error found
51
+ # @raise InvalidHeader if not jsonapi compliant
52
+ def check_content_type(env)
53
+ return if content_type_not_included_or_is_compliant?(env['CONTENT_TYPE'])
54
+
55
+ raise_error('Clients MUST send all JSON:API data in request documents with the header ' \
56
+ 'Content-Type: application/vnd.api+json without any media type parameters.',
57
+ 415)
58
+ end
59
+
60
+ # Checks to see the Accept header includes jsonapi without params
61
+ # @param (see #compliant?)
62
+ def check_accept(env)
63
+ return if env['HTTP_ACCEPT'].nil? || # no accept header means compliant
64
+ contains_at_least_one_jsonapi_media_type_without_params?(env['HTTP_ACCEPT'])
65
+
66
+ raise_error('Clients that include the JSON:API media type in their Accept header MUST ' \
67
+ 'specify the media type there at least once without any media type parameters.',
68
+ 406)
69
+ end
70
+
71
+ # @param content_type [String | NilClass] The http content-type header
72
+ # @return [TrueClass | FalseClass]
73
+ def content_type_not_included_or_is_compliant?(content_type)
74
+ content_type.nil? || content_type == 'application/vnd.api+json'
75
+ end
76
+
77
+ # Check the http verb against the content_type and accept header and raise
78
+ # error if the combination doesn't make sense
79
+ # @param (see #compliant?)
80
+ # @raise InvalidHeader the invalid header incombination with the http verb
81
+ def check_http_method_against_headers(env)
82
+ case env['REQUEST_METHOD']
83
+ when 'GET'
84
+ check_get_against_hdrs(env)
85
+ when 'POST' || 'PATCH' || 'PUT'
86
+ check_post_against_hdrs(env)
87
+ when 'DELETE'
88
+ check_delete_against_hdrs(env)
89
+ end
90
+ end
91
+
92
+ # Raise error if a GET request has a body or a content type header
93
+ # @param (see #compliant?)
94
+ def check_get_against_hdrs(env)
95
+ raise_error('GET requests cannot have a body.') unless env['rack.input'].nil?
96
+ raise_error("GET request cannot have a 'CONTENT_TYPE' http header.") unless env['CONTENT_TYPE'].nil?
97
+ end
98
+
99
+ # POST, PUT, and PATCH request must have a content type header,
100
+ # a body, and a content-type and accept header that accepts jsonapi
101
+ # @param (see #compliant?)
102
+ def check_post_against_hdrs(env)
103
+ raise_error("POST, PUT, and PATCH requests must have a 'CONTENT_TYPE' header.") unless env['CONTENT_TYPE']
104
+ raise_error('POST, PUT, and PATCH requests must have a body.') unless env['rack.input']
105
+
106
+ return if env['CONTENT_TYPE'] == 'application/vnd.api+json' && accepts_jsonapi?(env)
107
+
108
+ raise_error('POST, PUT, and PATCH requests must have an ACCEPT header that includes the ' \
109
+ "JSON:API media type, if they include a JSON:API 'CONTENT_TYPE' header")
110
+ end
111
+
112
+ # Check the accept header to see if any of the provided media types indicate that
113
+ # jsonapi is accepted
114
+ # @param (see #compliant?)
115
+ def accepts_jsonapi?(env)
116
+ return true if env['HTTP_ACCEPT'].nil?
117
+
118
+ env['HTTP_ACCEPT'].split(',').each do |mt|
119
+ return true if JSONAPI_MEDIA_TYPES.include?(mt)
120
+ end
121
+ false
122
+ end
123
+
124
+ # Raise error if DELETE hdr has a body or a content type header
125
+ def check_delete_against_hdrs(env)
126
+ raise_error('DELETE requests cannot have a body.') unless env['rack.input'].nil?
127
+ raise_error("DELETE request cannot have a 'CONTENT_TYPE' http header.") unless env['CONTENT_TYPE'].nil?
128
+ end
129
+
130
+ # @param accept_hdr [String] The value of the http accept header
131
+ def contains_at_least_one_jsonapi_media_type_without_params?(accept_hdr)
132
+ accept_hdr.split(',').each do |mt|
133
+ if mt == 'application/vnd.api+json'
134
+ return true
135
+ end
136
+ end
137
+ false
138
+ end
139
+
140
+ # Is the media type jsonapi and does it have included parameters
141
+ # @param media_type [String] One of the accepted media types
142
+ # @return [TrueClass | FalseClass]
143
+ def jsonapi_and_has_params?(media_type)
144
+ media_type_split = media_type.split(';')
145
+ JSONAPI_MEDIA_TYPES.include?(media_type_split.first) && \
146
+ contains_media_type_params?(media_type_split)
147
+ end
148
+
149
+ # @param msg [String] The message to raise InvalidHeader with.
150
+ def raise_error(msg, status_code = 400)
151
+ raise InvalidHeader.new(status_code), msg
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSONAPI
4
+ module Exceptions
5
+
6
+ # Checking for JSONAPI naming rules compliance
7
+ module NamingExceptions
8
+
9
+ # JSONAPI member names can only contain a-z, A-Z, 0-9, '-', '_', and the last two cannot be used
10
+ # at the start or end of a member name.
11
+ # @param name [String] The string to check for member name rule compliance
12
+ # @return
13
+ def self.check_member_constraints(name)
14
+ name = name.to_s
15
+ return 'Member names MUST contain at least one character' if name == ''
16
+ unless (name =~ /[^a-zA-Z0-9_-]/).nil?
17
+ return 'Member names MUST contain only the allowed characters: ' \
18
+ "a-z, A-Z, 0-9, '-', '_'"
19
+ end
20
+ unless (name[0] =~ /[-_]/).nil? && (name[-1] =~ /[-_]/).nil?
21
+ return 'Member names MUST start and end with a globally allowed character'
22
+ end
23
+ nil
24
+ end
25
+
26
+ # JSONAPI implementation specific query parameters follow the same constraints as member names
27
+ # with the additional requirement that they must also contain at least one non a-z character.
28
+ # @param name [String] The string to check for
29
+ def self.check_additional_constraints(name)
30
+ name = name.to_s
31
+ return nil unless (name =~ /[^a-z]/).nil?
32
+ 'Implementation specific query parameters MUST contain at least one non a-z character'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/exceptions/naming_exceptions'
4
+ require 'easy/jsonapi/exceptions/user_defined_exceptions'
5
+
6
+ module JSONAPI
7
+ module Exceptions
8
+
9
+ # Validates that the Query Parameters comply with the JSONAPI specification
10
+ module QueryParamsExceptions
11
+
12
+ # A more specific Standard Error to raise
13
+ class InvalidQueryParameter < StandardError
14
+ attr_accessor :status_code
15
+
16
+ # Init w a status code, so that it can be accessed when rescuing an exception
17
+ def initialize(status_code)
18
+ @status_code = status_code
19
+ super
20
+ end
21
+ end
22
+
23
+ # The jsonapi specific query parameters.
24
+ SPECIAL_QUERY_PARAMS = %i[include fields page sort filter].freeze
25
+
26
+ # Checks to see if the query paramaters conform to the JSONAPI spec and raises InvalidQueryParameter
27
+ # if any parts are found to be non compliant
28
+ # @param rack_req_params [Hash] The hash of the query parameters given by Rack::Request
29
+ def self.check_compliance(rack_req_params, config_manager = nil, opts = {})
30
+ impl_spec_names = rack_req_params.keys - %w[include fields page sort filter]
31
+ impl_spec_names.each do |name|
32
+ check_param_name(name)
33
+ end
34
+
35
+ err_msg = JSONAPI::Exceptions::UserDefinedExceptions.check_user_query_param_requirements(rack_req_params, config_manager, opts)
36
+ return err_msg unless err_msg.nil?
37
+
38
+ nil
39
+ end
40
+
41
+ # Checks an implementation specific param name to see if it complies to the spec.
42
+ def self.check_param_name(name)
43
+ should_return =
44
+ NamingExceptions.check_member_constraints(name).nil? && \
45
+ NamingExceptions.check_additional_constraints(name).nil? && \
46
+ !name.include?('-')
47
+ return if should_return
48
+
49
+ raise_error(
50
+ 'Implementation specific query parameters MUST adhere to the same constraints ' \
51
+ 'as member names. Allowed characters are: a-z, A-Z, 0-9 for beginning, middle, or end characters, ' \
52
+ "and '_' is allowed for middle characters. (While the JSON:API spec also allows '-', it is not " \
53
+ 'recommended, and thus is prohibited in this implementation). ' \
54
+ 'Implementation specific query members MUST contain at least one non a-z character as well. ' \
55
+ "Param name given: \"#{name}\""
56
+ )
57
+ end
58
+
59
+ # @param msg [String] The message to raise InvalidQueryParameter with.
60
+ def self.raise_error(msg, status_code = 400)
61
+ raise InvalidQueryParameter.new(status_code), msg
62
+ end
63
+
64
+ private_class_method :raise_error
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'easy/jsonapi/exceptions/document_exceptions'
4
+ require 'easy/jsonapi/exceptions/headers_exceptions'
5
+ require 'easy/jsonapi/exceptions/query_params_exceptions'
6
+
7
+ module JSONAPI
8
+ module Exceptions
9
+ # Allows a user of the gem to define extra requirements they want the middlewar to check for.
10
+ module UserDefinedExceptions
11
+
12
+ # Invaliid Document Error when checking user defined restrictions
13
+ class InvalidComponent < StandardError
14
+ attr_accessor :status_code, :msg
15
+
16
+ def initialize(err)
17
+ @msg = err[0]
18
+ @status_code = err[1] || 400
19
+ super
20
+ end
21
+ end
22
+
23
+ # The type of UserDefinedExceptions Error
24
+ class InvalidDocument < InvalidComponent
25
+ end
26
+
27
+ # The type of UserDefinedExceptions Error
28
+ class InvalidHeader < InvalidComponent
29
+ end
30
+
31
+ # The type of UserDefinedExceptions Error
32
+ class InvalidQueryParam < InvalidComponent
33
+ end
34
+
35
+ class << self
36
+
37
+ # Performs compliance checks on the document to see if it complies
38
+ # to the user defined requirements
39
+ # @param document [Hash] The hash representation of the json body
40
+ # @param config_manager [JSONAPI::ConfigManager] The config_manager option to retreive user
41
+ # requirements from
42
+ # @return [NilClass | String] Nil or the String Error Message
43
+ def check_user_document_requirements(document, config_manager, opts)
44
+ return if config_manager.nil?
45
+
46
+ config = get_config(config_manager, opts[:http_method], opts[:path])
47
+ err = check_for_client_generated_id(document, config.allow_client_ids, opts[:http_method])
48
+
49
+ return if config.default? && config_manager.size.positive?
50
+
51
+ if err.nil?
52
+ err = check_for_required_document_members(document, config.required_document_members)
53
+ end
54
+ # To add more user requirement features, add more methods here
55
+
56
+ JSONAPI::Exceptions::UserDefinedExceptions::InvalidDocument.new(err) unless err.nil?
57
+ end
58
+
59
+ # Performs compliance checks on the headers to see if it complies
60
+ # to the user defined requirements
61
+ # @param headers [Hash | JSONAPI::HeaderCollection] The collection of provided headers. Keys should be upper case strings
62
+ # with underscores instead of dashes.
63
+ # @param config (see #check_user_document_requirements)
64
+ def check_user_header_requirements(headers, config_manager, opts)
65
+ return if config_manager.nil? || config_manager.default?
66
+ config = get_config(config_manager, opts[:http_method], opts[:path])
67
+
68
+ err =
69
+ check_for_required_headers(headers, config.required_headers)
70
+ # To add more user requirement features, add more methods here
71
+
72
+ JSONAPI::Exceptions::UserDefinedExceptions::InvalidHeader.new(err) unless err.nil?
73
+ end
74
+
75
+ # Performs compliance checks on the query params to see if it complies
76
+ # to the user defined requirements
77
+ # @param rack_req_params [Hash] The hash of the query parameters given by Rack::Request
78
+ # @param config (see #check_user_document_requirements)
79
+ def check_user_query_param_requirements(rack_req_params, config_manager, opts)
80
+ return if config_manager.nil? || config_manager.default?
81
+ config = get_config(config_manager, opts[:http_method], opts[:path])
82
+
83
+ err =
84
+ check_for_required_params(rack_req_params, config.required_query_params)
85
+ # To add more user requirement features, add more methods here
86
+
87
+ JSONAPI::Exceptions::UserDefinedExceptions::InvalidQueryParam.new(err) unless err.nil?
88
+ end
89
+
90
+ private
91
+
92
+ # ***************************
93
+ # * Document Helper Methods *
94
+ # ***************************
95
+
96
+ # Checks to see whether the document conatians all the require members
97
+ # See spec file for more examples.
98
+ # Ex:
99
+ # {
100
+ # data:
101
+ # [
102
+ # {
103
+ # type: nil,
104
+ # attributes: { a1: nil, a2: nil }
105
+ # }
106
+ # ],
107
+ # meta:
108
+ # {
109
+ # m1: nil
110
+ # }
111
+ # }
112
+ # @param document (see #check_user_document_requirements)
113
+ # @param req_mems[Hash] The hash representation of the user-defined required json members.
114
+ def check_for_required_document_members(document, req_mems)
115
+ if reached_value_to_check?(document, req_mems)
116
+ return check_values(document, req_mems)
117
+ end
118
+
119
+ err = check_structure(document, req_mems)
120
+ return err unless err.nil?
121
+
122
+ case req_mems
123
+ when Hash
124
+ req_mems.each do |k, v|
125
+ err = check_for_required_document_members(document[k], v)
126
+ return err unless err.nil?
127
+ end
128
+ when Array
129
+ document.each do |m|
130
+ err = check_for_required_document_members(m, req_mems.first)
131
+ return err unless err.nil?
132
+ end
133
+ end
134
+ nil
135
+ end
136
+
137
+ # Check if same class or if req_mems nil. If not it indicates the user has specified set
138
+ # of required values for a given key. Check whether the current valueis within the set.
139
+ # @param (see #check_required_document_members)
140
+ # @return [NilClass | String] An error message if one found.
141
+ def check_structure(document, req_mems)
142
+ if both_are_hashes(document, req_mems)
143
+ doc_keys = document.keys
144
+ req_keys = req_mems.keys
145
+ return if doc_keys & req_keys == req_keys
146
+ ["Document is missing user-defined required keys: #{req_keys - doc_keys}"]
147
+ else
148
+ return if document.instance_of?(req_mems.class) || req_mems.nil?
149
+ ["User-defined required members hash does not mimic structure of json document: #{document}"]
150
+ end
151
+ end
152
+
153
+ def both_are_hashes(first, second)
154
+ first.is_a?(Hash) && second.is_a?(Hash)
155
+ end
156
+
157
+ # @return [TrueClass | FalseClass]
158
+ def reached_value_to_check?(document, req_mems)
159
+ (req_mems.is_a?(Proc) || req_mems.nil?) && !document.is_a?(Hash) && !document.is_a?(Array)
160
+ end
161
+
162
+ # Checks whether a value given is within the permitted values
163
+ # @param value_given [Any]
164
+ def check_values(value_given, permitted_values)
165
+ return if permitted_values.nil? || (permitted_values.is_a?(Proc) && permitted_values.call(value_given))
166
+ ["The user-defined Proc found at #{permitted_values.source_location}, evaluated the given value, #{value_given}, to be non compliant."]
167
+ end
168
+
169
+ # Checks if a resource id was included in the primary resource sent in a POST request
170
+ # @param document (see# #check_required_document_members)
171
+ # @param allow_client_ids [TrueClass | FalseClass] Does the user allow client generated
172
+ # ids
173
+ # @param http_method [String] Does the document belong to a POST request
174
+ def check_for_client_generated_id(document, allow_client_ids, http_method)
175
+ return unless http_method == 'POST' && !allow_client_ids
176
+ return unless JSONAPI::Utility.all_hash_path?(document, %i[data id])
177
+
178
+ msg = 'Document MUST return 403 Forbidden in response to an unsupported request ' \
179
+ 'to create a resource with a client-generated ID.'
180
+ [msg, 403]
181
+ end
182
+
183
+ # *************************
184
+ # * Header Helper Methods *
185
+ # *************************
186
+
187
+ # Checks to makes sure the headers conatin the user defined required headers
188
+ # @param headers [Hash] The provided headers
189
+ # @param req_headers [Hash] The required headers
190
+ def check_for_required_headers(headers, req_headers)
191
+ return if req_headers.nil?
192
+
193
+ req_headers.each do |hdr|
194
+ h_name = hdr.to_s.upcase.gsub(/-/, '_')
195
+ unless headers[h_name]
196
+ return ["Headers missing one of the user-defined required headers: #{h_name}"]
197
+ end
198
+ end
199
+ nil
200
+ end
201
+
202
+ # ******************************
203
+ # * Query Param Helper Methods *
204
+ # ******************************
205
+
206
+ # Checks to make sure the query params contain the user defined required params
207
+ # Rack Request Params Ex:
208
+ # {
209
+ # 'fields' => { 'articles' => 'title,body,author', 'people' => 'name' },
210
+ # 'include' => 'author,comments-likers,comments.users',
211
+ # 'josh_ua' => 'demoss,simpson',
212
+ # 'page' => { 'offset' => '5', 'limit' => '20' },
213
+ # 'filter' => { 'comments' => '(author/age > 21)', 'users' => '(age < 15)' },
214
+ # 'sort' => 'age,title'
215
+ # }
216
+ # Required Params Hash Ex:
217
+ # {
218
+ # fields: { articles: nil },
219
+ # include: nil
220
+ # page: nil
221
+ # }
222
+ # @param rack_req_params [Hash] The Rack::Request.params hash
223
+ # @param required_params [Hash] The user defined required query params
224
+ def check_for_required_params(rack_req_params, required_params)
225
+ return if required_params.nil?
226
+
227
+ case required_params
228
+ when Hash
229
+ required_params.each do |k, v|
230
+ return ["Query Params missing one of the user-defined required query params: #{k}"] unless rack_req_params[k.to_s]
231
+ err = check_for_required_params(rack_req_params[k.to_s], v)
232
+ return err unless err.nil?
233
+ end
234
+ when nil
235
+ return
236
+ else
237
+ return ['The user-defined required query params hash must contain keys with values either hash or nil']
238
+ end
239
+ nil
240
+ end
241
+
242
+ def get_config(config_manager, http_method, path)
243
+ if http_method
244
+ res_type = JSONAPI::Utility.path_to_res_type(path)
245
+ config_manager[res_type] || config_manager.global
246
+ else
247
+ config_manager.global
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+ end