apimatic_core 0.1.0 → 0.2.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 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