apimatic_core 0.1.0 → 0.2.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: c581361453ae63c619ca7b7bcc5b2e4cb5cd2afe4ab1f7584462a0698ab8be3d
4
- data.tar.gz: 51c745d2ac8600da653324628f1cf4e00e2059ceb12e22f150f49ec2ed3a9f03
3
+ metadata.gz: 43a0787b023f7ffa1b7c2a85955b951607f73678560cd1ae5ed5093a98e39787
4
+ data.tar.gz: 6753fbba0aeb9f496c4a926a226b55fe6aa6245b836ac027fd86d0b9cb295a12
5
5
  SHA512:
6
- metadata.gz: 1a87d5f62193c6f46e4fda2289d5c4a8cae5d716ee35723f030d6ad97ffc569ffec23019e89065a5882ea806aec791ea121bc8f0e326496a923a49feb0c1b484
7
- data.tar.gz: 44acde48930a6e621608d30fb646e9f467e0fba89c07520016e55e2b017b17f239ede9bcc0de1e79537e5303da469b91f1fd2f1844a212f10bd92a40b48cd103
6
+ metadata.gz: a3d9f9fe3b80a7980b632ac2a6b3fa34780aec0e94cefc7f8f0f1a4a632e99e0da7b2d66cf48f279356cee3486c4595e1ebe48764afcaa7344562732a91ea7f2
7
+ data.tar.gz: 125c4d4c4dadcd5c069b78eed5b703493e46e666575d1e926acb0e7a4b62099627ed97d8703b502e5e2d476edbf14e34f4438b4733198e00ca5b13a8378cbbf6
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # apimatic-core
2
- [//]: # ([![PyPI][rubygems-version]][rubygems-apimatic-faraday-client-adapter-url])
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/apimatic_core.svg)](https://badge.fury.io/rb/apimatic_core)
3
4
  [![Tests][test-badge]][test-url]
4
5
  [![Linting][lint-badge]][lint-url]
5
- [![Maintainability][maintainability-url]][code-climate-url]
6
6
  [![Test Coverage][test-coverage-url]][code-climate-url]
7
7
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
8
8
  [![Licence][license-badge]][license-url]
@@ -86,10 +86,8 @@ gem 'apimatic_core'
86
86
  | [`XmlHelper`](lib/apimatic-core/utilities/xml_helper.rb ) | A Helper class that holds utility methods for xml serialization and deserialization. |
87
87
 
88
88
  ## Links
89
- * [apimatic_core_interfaces](link here)
89
+ * [apimatic_core_interfaces](https://rubygems.org/gems/apimatic_core_interfaces)
90
90
 
91
- [rubygems-version]: https://img.shields.io/pypi/v/apimatic-requests-client-adapter
92
- [rubygems-apimatic-faraday-client-adapter-url]: https://pypi.org/project/apimatic-requests-client-adapter/
93
91
  [test-badge]: https://github.com/apimatic/core-lib-ruby/actions/workflows/test-runner.yml/badge.svg
94
92
  [test-url]: https://github.com/apimatic/core-lib-ruby/actions/workflows/test-runner.yml
95
93
  [lint-badge]: https://github.com/apimatic/core-lib-ruby/actions/workflows/lint-runner.yml/badge.svg
@@ -44,13 +44,27 @@ module CoreLibrary
44
44
  self
45
45
  end
46
46
 
47
- # Sets local_errors hash key value.
47
+ # Registers an entry with error message in the local errors hash.
48
48
  # @param [String] error_code The error code to check against.
49
- # @param [String] description The reason for the exception.
49
+ # @param [String] error_message The reason for the exception.
50
50
  # @param [ApiException] exception_type The type of the exception to raise.
51
51
  # @return [ResponseHandler] An updated instance of ResponseHandler.
52
- def local_error(error_code, description, exception_type)
53
- @local_errors[error_code.to_s] = ErrorCase.new.description(description).exception_type(exception_type)
52
+ def local_error(error_code, error_message, exception_type)
53
+ @local_errors[error_code.to_s] = ErrorCase.new
54
+ .error_message(error_message)
55
+ .exception_type(exception_type)
56
+ self
57
+ end
58
+
59
+ # Registers an entry with error template in the local errors hash.
60
+ # @param [String] error_code The error code to check against.
61
+ # @param [String] error_message_template The reason template for the exception.
62
+ # @param [ApiException] exception_type The type of the exception to raise.
63
+ # @return [ResponseHandler] An updated instance of ResponseHandler.
64
+ def local_error_template(error_code, error_message_template, exception_type)
65
+ @local_errors[error_code.to_s] = ErrorCase.new
66
+ .error_message_template(error_message_template)
67
+ .exception_type(exception_type)
54
68
  self
55
69
  end
56
70
 
@@ -186,34 +200,16 @@ module CoreLibrary
186
200
  end
187
201
  # rubocop:enable Style/OptionalBooleanParameter
188
202
 
189
- # Validates the response provided and throws an error from global_errors if it fails.
190
- # @param response The received response.
191
- # @param global_errors Global errors hash.
203
+ # Validates the response provided and throws an error against the configured status code.
204
+ # @param [HttpResponse] response The received response.
205
+ # @param [Hash] global_errors Global errors hash.
206
+ # @raise [ApiException] Throws the exception when the response contains errors.
192
207
  def validate(response, global_errors)
193
- return unless response.status_code < 200 || response.status_code > 208
194
-
195
- actual_status_code = response.status_code.to_s
196
-
197
- contains_local_errors = (!@local_errors.nil? and !@local_errors[actual_status_code].nil?)
198
- if contains_local_errors
199
- error_case = @local_errors[actual_status_code]
200
- raise error_case.get_exception_type.new error_case.get_description, response
201
- end
202
-
203
- contains_local_default_error = (!@local_errors.nil? and !@local_errors['default'].nil?)
204
- if contains_local_default_error
205
- error_case = @local_errors['default']
206
- raise error_case.get_exception_type.new error_case.get_description, response
207
- end
208
+ return unless response.status_code < 200 || response.status_code > 299
208
209
 
209
- contains_global_errors = (!global_errors.nil? and !global_errors[actual_status_code].nil?)
210
- if contains_global_errors
211
- error_case = global_errors[actual_status_code]
212
- raise error_case.get_exception_type.new error_case.get_description, response
213
- end
210
+ validate_against_error_cases(response, @local_errors)
214
211
 
215
- error_case = global_errors['default']
216
- raise error_case.get_exception_type.new error_case.get_description, response unless error_case.nil?
212
+ validate_against_error_cases(response, global_errors)
217
213
  end
218
214
 
219
215
  # Applies xml deserializer to the response.
@@ -265,5 +261,31 @@ module CoreLibrary
265
261
 
266
262
  deserialized_value
267
263
  end
264
+
265
+ # Validates the response against the provided error cases hash, if matches, it raises the exception.
266
+ # @param [HttpResponse] response The received response.
267
+ # @param [Hash] error_cases The error cases hash.
268
+ # @raise [ApiException] Raises the APIException when configured error code matches.
269
+ def validate_against_error_cases(response, error_cases)
270
+ actual_status_code = response.status_code.to_s
271
+
272
+ # Handling error case when configured as explicit error code
273
+ error_case = error_cases[actual_status_code]
274
+ error_case&.raise_exception(response)
275
+
276
+ # Handling error case when configured as explicit error codes range
277
+ default_range_entry = error_cases&.filter do |error_code, _|
278
+ error_code.match?("^#{actual_status_code[0]}XX$")
279
+ end
280
+
281
+ default_range_error_case = default_range_entry&.map { |_, error_case_instance| error_case_instance }
282
+
283
+ default_range_error_case[0].raise_exception(response) unless
284
+ default_range_error_case.nil? || default_range_error_case.empty?
285
+
286
+ # Handling default error case if configured
287
+ default_error_case = error_cases['default']
288
+ default_error_case&.raise_exception(response)
289
+ end
268
290
  end
269
291
  end
@@ -2,22 +2,25 @@ module CoreLibrary
2
2
  # This data class represents the expected errors to be handled after the API call.
3
3
  class ErrorCase
4
4
  def initialize
5
- @description = nil
5
+ @error_message = nil
6
+ @error_message_template = nil
6
7
  @exception_type = nil
7
8
  end
8
9
 
9
10
  # The setter for the description of the error message.
10
- # @param [String] description The description of the error message.
11
+ # @param [String] error_message The error message.
11
12
  # @return [ErrorCase] An updated instance of ErrorCase.
12
- def description(description)
13
- @description = description
13
+ def error_message(error_message)
14
+ @error_message = error_message
14
15
  self
15
16
  end
16
17
 
17
- # The getter for the description of the error message.
18
- # @return [String] The description of the error message.
19
- def get_description
20
- @description
18
+ # The setter for the description of the error message.
19
+ # @param [String] error_message_template The error message template.
20
+ # @return [ErrorCase] An updated instance of ErrorCase.
21
+ def error_message_template(error_message_template)
22
+ @error_message_template = error_message_template
23
+ self
21
24
  end
22
25
 
23
26
  # The setter for the type of the exception to be thrown.
@@ -28,10 +31,48 @@ module CoreLibrary
28
31
  self
29
32
  end
30
33
 
31
- # The getter for the type of the exception to be thrown.
32
- # @return [Object] The type of the exception to be thrown.
33
- def get_exception_type
34
- @exception_type
34
+ # Getter for the error message for the exception case. This considers both error message
35
+ # and error template message. Error message template has the higher precedence over an error message.
36
+ # @param response The received http response.
37
+ # @return [String] The resolved exception message.
38
+ def get_error_message(response)
39
+ return _get_resolved_error_message_template(response) unless @error_message_template.nil?
40
+
41
+ @error_message
42
+ end
43
+
44
+ # Raises the exception for the current error case type.
45
+ # @param response The received response.
46
+ def raise_exception(response)
47
+ raise @exception_type.new get_error_message(response), response
48
+ end
49
+
50
+ # Updates all placeholders in the given message template with provided value.
51
+ # @param response The received http response.
52
+ # @return [String] The resolved template message.
53
+ def _get_resolved_error_message_template(response)
54
+ placeholders = @error_message_template.scan(/{\$.*?\}/)
55
+
56
+ status_code_placeholder = placeholders.select { |element| element == '{$statusCode}' }.uniq
57
+ header_placeholders = placeholders.select { |element| element.start_with?('{$response.header') }.uniq
58
+ body_placeholders = placeholders.select { |element| element.start_with?('{$response.body') }.uniq
59
+
60
+ # Handling response code placeholder
61
+ error_message_template = ApiHelper.resolve_template_placeholders(status_code_placeholder,
62
+ response.status_code.to_s,
63
+ @error_message_template)
64
+
65
+ # Handling response header placeholder
66
+ error_message_template = ApiHelper.resolve_template_placeholders(header_placeholders, response.headers,
67
+ error_message_template)
68
+
69
+ # Handling response body placeholder
70
+ response_payload = ApiHelper.json_deserialize(response.raw_body, true)
71
+ error_message_template = ApiHelper.resolve_template_placeholders_using_json_pointer(body_placeholders,
72
+ response_payload,
73
+ error_message_template)
74
+
75
+ error_message_template
35
76
  end
36
77
  end
37
78
  end
@@ -1,4 +1,6 @@
1
1
  require 'erb'
2
+ require 'json-pointer'
3
+
2
4
  module CoreLibrary
3
5
  # API utility class involved in executing an API
4
6
  class ApiHelper
@@ -230,9 +232,13 @@ module CoreLibrary
230
232
  # @param [String] json A JSON string.
231
233
  # rubocop:disable Style/OptionalBooleanParameter
232
234
  def self.json_deserialize(json, should_symbolize = false)
233
- JSON.parse(json, symbolize_names: should_symbolize)
234
- rescue StandardError
235
- raise TypeError, 'Server responded with invalid JSON.'
235
+ return if json.nil?
236
+
237
+ begin
238
+ JSON.parse(json, symbolize_names: should_symbolize)
239
+ rescue StandardError
240
+ raise TypeError, 'Server responded with invalid JSON.'
241
+ end
236
242
  end
237
243
  # rubocop:enable Style/OptionalBooleanParameter
238
244
 
@@ -547,5 +553,54 @@ module CoreLibrary
547
553
  TrueClass, FalseClass, Date,
548
554
  DateTime, Array, Hash, Object]
549
555
  end
556
+
557
+ # Updates all placeholders in the given message template with provided value.
558
+ # @param [String] placeholders The placeholders that need to be searched and replaced in the given template value.
559
+ # @param [String] value The dictionary containing the actual values to replace with.
560
+ # @param [String] template The template string containing placeholders.
561
+ # @@return [String] The resolved template value.
562
+ def self.resolve_template_placeholders_using_json_pointer(placeholders, value, template)
563
+ placeholders.each do |placeholder|
564
+ extracted_value = ''
565
+ if placeholder.include? '#'
566
+ # pick the 2nd chunk then remove the last character (i.e. `}`) of the string value
567
+ node_pointer = placeholder.split('#')[1].delete_suffix('}')
568
+ value_pointer = JsonPointer.new(value, node_pointer, symbolize_keys: true)
569
+ extracted_value = json_serialize(value_pointer.value) if value_pointer.exists?
570
+ elsif !value.nil?
571
+ extracted_value = json_serialize(value)
572
+ end
573
+ template.gsub!(placeholder, extracted_value)
574
+ end
575
+
576
+ template
577
+ end
578
+
579
+ # Updates all placeholders in the given message template with provided value.
580
+ # @param [List] placeholders The placeholders that need to be searched and replaced in the given template value.
581
+ # @param [Hash|String] values The value which refers to the actual values to replace with.
582
+ # @param [String] template The template string containing placeholders.
583
+ # @@return [String] The resolved template value.
584
+ def self.resolve_template_placeholders(placeholders, values, template)
585
+ values = values.map { |key, value| [key.to_s, value.to_s] }.to_h if values.is_a? Hash
586
+
587
+ placeholders.each do |placeholder|
588
+ extracted_value = ''
589
+ if values.is_a? Hash
590
+ # pick the last chunk then strip the last character (i.e. `}`) of the string value
591
+ key = if placeholder.include? '.'
592
+ placeholder.split('.')[-1].delete_suffix('}')
593
+ else
594
+ placeholder.delete_prefix('{').delete_suffix('}')
595
+ end
596
+ extracted_value = values[key] unless values[key].nil?
597
+ else
598
+ extracted_value = values unless values.nil?
599
+ end
600
+ template.gsub!(placeholder, extracted_value.to_s)
601
+ end
602
+
603
+ template
604
+ end
550
605
  end
551
606
  end
data/lib/apimatic_core.rb CHANGED
@@ -3,6 +3,7 @@ require 'json'
3
3
  require 'certifi'
4
4
  require 'apimatic_core_interfaces'
5
5
  require 'cgi'
6
+ require 'json-pointer'
6
7
 
7
8
  require_relative 'apimatic-core/request_builder'
8
9
  require_relative 'apimatic-core/response_handler'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apimatic_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - APIMatic Ltd.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-28 00:00:00.000000000 Z
11
+ date: 2023-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apimatic_core_interfaces
@@ -78,6 +78,20 @@ dependencies:
78
78
  - - "~>"
79
79
  - !ruby/object:Gem::Version
80
80
  version: '1.0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: json-pointer
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
81
95
  - !ruby/object:Gem::Dependency
82
96
  name: faraday
83
97
  requirement: !ruby/object:Gem::Requirement