apimatic_core 0.3.15 → 0.3.16

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.
@@ -0,0 +1,257 @@
1
+ module CoreLibrary
2
+ # The `JsonPointer` class provides a utility for querying, modifying, and deleting
3
+ # values within deeply nested Ruby Hashes and Arrays using JSON Pointer syntax (RFC 6901),
4
+ # extended with support for wildcards (`~`) and array-push semantics (`-`).
5
+ #
6
+ # ## Features
7
+ # - Navigate and retrieve deeply nested values using JSON Pointer paths.
8
+ # - Supports complex structures containing both Arrays and Hashes.
9
+ # - Wildcard support (`~`) for batch operations across multiple elements.
10
+ # - Special key (`-`) for appending to arrays (push behavior).
11
+ # - Optional `:symbolize_keys` behavior to convert pointer fragments to symbols.
12
+ #
13
+ # ## Example Usage
14
+ # data = { "a" => [{ "b" => 1 }, { "b" => 2 }] }
15
+ # pointer = JsonPointer.new(data, "/a/~1/b")
16
+ # value = pointer.value # => 2
17
+ #
18
+ # pointer.value = 42
19
+ # pointer.delete
20
+ #
21
+ # ## Limitations
22
+ # - This class operates directly on mutable input data structures.
23
+ # - Wildcards and array push keys are not part of the official JSON Pointer spec.
24
+ #
25
+ # @example Initialize and read value
26
+ # JsonPointer.new({ "foo" => { "bar" => 42 } }, "/foo/bar").value # => 42
27
+ #
28
+ class JsonPointer
29
+ NotFound = Class.new
30
+ WILDCARD = '~'.freeze
31
+ ARRAY_PUSH_KEY = '-'.freeze
32
+
33
+ def self.escape_fragment(fragment)
34
+ return fragment if fragment == WILDCARD
35
+
36
+ fragment.gsub(/~/, '~0').gsub(%r{/}, '~1')
37
+ end
38
+
39
+ def self.unescape_fragment(fragment)
40
+ fragment.gsub(/~1/, '/').gsub(/~0/, '~')
41
+ end
42
+
43
+ def self.join_fragments(fragments)
44
+ fragments.map { |f| escape_fragment(f) }.join('/')
45
+ end
46
+
47
+ def initialize(hash, path, options = {})
48
+ @hash = hash
49
+ @path = path
50
+ @options = options
51
+ end
52
+
53
+ def value
54
+ get_member_value
55
+ end
56
+
57
+ def value=(new_value)
58
+ set_member_value(new_value)
59
+ end
60
+
61
+ def delete
62
+ delete_member
63
+ end
64
+
65
+ def exists?
66
+ _exists = false
67
+ get_target_member(@hash, path_fragments.dup) do |target, options = {}|
68
+ if options[:wildcard]
69
+ _exists = target.any? { |t| !t.nil? && !t.is_a?(NotFound) }
70
+ else
71
+ _exists = true unless target.is_a?(NotFound)
72
+ end
73
+ end
74
+ _exists
75
+ end
76
+
77
+ private
78
+
79
+ def get_member_value(obj = @hash, fragments = path_fragments.dup)
80
+ return obj if fragments.empty?
81
+
82
+ fragment = fragments.shift
83
+ case obj
84
+ when Hash
85
+ get_member_value(obj[fragment_to_key(fragment)], fragments)
86
+ when Array
87
+ if fragment == WILDCARD
88
+ obj.map { |i| get_member_value(i, fragments.dup) }
89
+ else
90
+ get_member_value(obj[fragment_to_index(fragment)], fragments)
91
+ end
92
+ else
93
+ NotFound.new
94
+ end
95
+ end
96
+
97
+ def get_target_member(obj, fragments, options = {}, &block)
98
+ return yield(obj, {}) if fragments.empty?
99
+
100
+ case obj
101
+ when Hash
102
+ get_target_member_if_hash(obj, fragments, options, &block)
103
+ when Array
104
+ get_target_member_if_array(obj, fragments, options, &block)
105
+ else
106
+ NotFound.new
107
+ end
108
+ end
109
+
110
+ def get_target_member_if_hash(obj, fragments, options = {}, &block)
111
+ fragment = fragments.shift
112
+ key = fragment_to_key(fragment)
113
+ obj = if options[:create_missing]
114
+ obj[key] ||= {}
115
+ else
116
+ obj.key?(key) ? obj[key] : NotFound.new
117
+ end
118
+
119
+ get_target_member(obj, fragments, options, &block)
120
+ end
121
+
122
+ def get_target_member_if_array(obj, fragments, options = {}, &block)
123
+ fragment = fragments.shift
124
+ if fragment == WILDCARD
125
+ if obj.any?
126
+ targets = obj.map do |i|
127
+ get_target_member(i || {}, fragments.dup, options) { |t| t }
128
+ end
129
+ yield(targets, wildcard: true)
130
+ else
131
+ NotFound.new
132
+ end
133
+ else
134
+ index = fragment_to_index(fragment)
135
+ obj = if options[:create_missing]
136
+ obj[index] ||= {}
137
+ else
138
+ index >= obj.size ? NotFound.new : obj[index]
139
+ end
140
+
141
+ get_target_member(obj, fragments, &block)
142
+ end
143
+ end
144
+
145
+ def set_member_value(new_value)
146
+ obj = @hash
147
+ fragments = path_fragments.dup
148
+
149
+ return if fragments.empty?
150
+
151
+ target_fragment = fragments.pop
152
+ target_parent_fragment = fragments.pop if target_fragment == ARRAY_PUSH_KEY
153
+
154
+ get_target_member(obj, fragments.dup, create_missing: true) do |target, options = {}|
155
+ if options[:wildcard]
156
+ fragments = fragments.each_with_object([]) do |memo, f|
157
+ break memo if f == WILDCARD
158
+
159
+ memo << f
160
+ memo
161
+ end
162
+
163
+ path = join_fragments(fragments)
164
+ pointer = self.class.new(obj, path, @options)
165
+ pointer.value.push({ fragment_to_key(target_fragment) => new_value })
166
+ elsif target_fragment == ARRAY_PUSH_KEY
167
+ key = case target
168
+ when Hash
169
+ fragment_to_key(target_parent_fragment)
170
+ when Array
171
+ fragment_to_index(target_parent_fragment)
172
+ else
173
+ nil
174
+ end
175
+
176
+ return unless key
177
+
178
+ target[key] ||= []
179
+ return unless target[key].is_a?(Array)
180
+
181
+ target[key].push(new_value)
182
+ return new_value
183
+ else
184
+ case target
185
+ when Hash
186
+ target[fragment_to_key(target_fragment)] = new_value
187
+ when Array
188
+ # NOTE: Using `Array#insert(index, value)` here shifts existing elements to the right
189
+ # instead of replacing the value at the index. If the index is out of bounds,
190
+ # it fills the gap with `nil`.
191
+ target.insert(fragment_to_index(target_fragment), new_value)
192
+ else
193
+ nil
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ def delete_member
200
+ obj = @hash
201
+ fragments = path_fragments.dup
202
+
203
+ return if fragments.empty?
204
+
205
+ target_fragment = fragments.pop
206
+ get_target_member(obj, fragments) do |target, options = {}|
207
+ if options[:wildcard]
208
+ target.each do |t|
209
+ case t
210
+ when Hash
211
+ t.delete(fragment_to_key(target_fragment))
212
+ else
213
+ nil
214
+ end
215
+ end
216
+ else
217
+ case target
218
+ when Hash
219
+ target.delete(fragment_to_key(target_fragment))
220
+ when Array
221
+ if target_fragment == WILDCARD
222
+ target.replace([])
223
+ else
224
+ target.delete_at(fragment_to_index(target_fragment))
225
+ end
226
+ else
227
+ nil
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ def path_fragments
234
+ @path_fragments ||= @path.sub(%r{\A/}, '').split('/').map { |fragment| unescape_fragment(fragment) }
235
+ end
236
+
237
+ def escape_fragment(fragment)
238
+ JsonPointer.escape_fragment(fragment)
239
+ end
240
+
241
+ def unescape_fragment(fragment)
242
+ JsonPointer.unescape_fragment(fragment)
243
+ end
244
+
245
+ def join_fragments(fragments)
246
+ JsonPointer.join_fragments(fragments)
247
+ end
248
+
249
+ def fragment_to_key(fragment)
250
+ @options[:symbolize_keys] ? fragment.to_sym : fragment
251
+ end
252
+
253
+ def fragment_to_index(fragment)
254
+ fragment.to_i
255
+ end
256
+ end
257
+ end
@@ -1,134 +1,58 @@
1
1
  module CoreLibrary
2
2
  # A utility for json specific operations.
3
3
  class JsonPointerHelper
4
- NotFound = Class.new
5
- WILDCARD = '~'.freeze
6
- ARRAY_PUSH_KEY = '-'.freeze
7
-
8
- def self.escape_fragment(fragment)
9
- return fragment if fragment == WILDCARD
10
-
11
- fragment.gsub(/~/, '~0').gsub(%r{/}, '~1')
12
- end
13
-
14
- def self.unescape_fragment(fragment)
15
- fragment.gsub(/~1/, '/').gsub(/~0/, '~')
16
- end
17
-
18
- def initialize(hash, path, options = {})
19
- @hash = hash
20
- @path = path
21
- @options = options
22
- end
23
-
24
- def value
25
- get_member_value
26
- end
27
-
28
- def exists?
29
- _exists = false
30
- get_target_member(@hash, path_fragments.dup) do |target, options = {}|
31
- if options[:wildcard]
32
- _exists = target.any? { |t| !t.nil? && !t.is_a?(NotFound) }
33
- else
34
- _exists = true unless target.is_a?(NotFound)
35
- end
36
- end
37
- _exists
38
- end
39
-
40
- private
41
-
42
- def get_member_value(obj = @hash, fragments = path_fragments.dup)
43
- return obj if fragments.empty?
44
-
45
- fragment = fragments.shift
46
- case obj
47
- when Hash
48
- get_member_value(obj[fragment_to_key(fragment)], fragments)
49
- when Array
50
- if fragment == WILDCARD
51
- obj.map { |i| get_member_value(i, fragments.dup) }
52
- else
53
- get_member_value(obj[fragment_to_index(fragment)], fragments)
54
- end
55
- else
56
- NotFound.new
57
- end
58
- end
59
-
60
- def get_target_member(obj, fragments, options = {}, &block)
61
- return yield(obj, {}) if fragments.empty?
62
-
63
- case obj
64
- when Hash
65
- get_target_member_if_hash(obj, fragments, options, &block)
66
- when Array
67
- get_target_member_if_array(obj, fragments, options, &block)
68
- else
69
- NotFound.new
4
+ # Splits a JSON pointer string into its prefix and field path components.
5
+ #
6
+ # @param [String, nil] json_pointer The JSON pointer string to split.
7
+ # @return [Array(String, String), Array(nil, nil)] A tuple with path prefix and field path,
8
+ # or [nil, nil] if input is nil or empty.
9
+ def self.split_into_parts(json_pointer)
10
+ return [nil, nil] if json_pointer.nil? || json_pointer.strip.empty?
11
+
12
+ path_prefix, field_path = json_pointer.split('#', 2)
13
+ field_path ||= ''
14
+
15
+ [path_prefix, field_path]
16
+ end
17
+
18
+ # Retrieves a value from a hash using a JSON pointer.
19
+ #
20
+ # @param [Hash] hash The input hash to search.
21
+ # @param [String] pointer The JSON pointer string (e.g. "#/a/b").
22
+ # @param [Boolean] symbolize_keys Whether to symbolize keys in the hash while resolving.
23
+ #
24
+ # @return [Object, nil] The value at the given pointer path, or nil if not found or invalid.
25
+ def self.get_value_by_json_pointer(hash, pointer, symbolize_keys: false)
26
+ return nil if hash.nil? || pointer.nil? || pointer.strip.empty?
27
+
28
+ begin
29
+ json_pointer_resolver = JsonPointer.new(hash, pointer, symbolize_keys: symbolize_keys)
30
+ _value = json_pointer_resolver.value
31
+ _value.is_a?(JsonPointer::NotFound) ? nil : _value
32
+ rescue StandardError
33
+ # Optionally log error or re-raise specific known ones
34
+ nil
70
35
  end
71
36
  end
72
37
 
73
- def get_target_member_if_hash(obj, fragments, options = {}, &block)
74
- fragment = fragments.shift
75
- key = fragment_to_key(fragment)
76
- obj = if options[:create_missing]
77
- obj[key] ||= {}
78
- else
79
- obj.key?(key) ? obj[key] : NotFound.new
80
- end
81
-
82
- get_target_member(obj, fragments, options, &block)
83
- end
84
-
85
- def get_target_member_if_array(obj, fragments, options = {}, &block)
86
- fragment = fragments.shift
87
- if fragment == WILDCARD
88
- if obj.any?
89
- targets = obj.map do |i|
90
- get_target_member(i || {}, fragments.dup, options) do |t|
91
- t
92
- end
93
- end
94
- yield(targets, wildcard: true)
95
- else
96
- NotFound.new
97
- end
98
- else
99
- index = fragment_to_index(fragment)
100
- obj = if options[:create_missing]
101
- obj[index] ||= {}
102
- else
103
- index >= obj.size ? NotFound.new : obj[index]
104
- end
105
-
106
- get_target_member(obj, fragments, &block)
38
+ # Retrieves a value from a hash using a JSON pointer.
39
+ #
40
+ # @param [Hash] hash The input hash to search.
41
+ # @param [String] pointer The JSON pointer string (e.g. "#/a/b").
42
+ # @param [Boolean] symbolize_keys Whether to symbolize keys in the hash while resolving.
43
+ #
44
+ # @return [Object, nil] The value at the given pointer path, or nil if not found or invalid.
45
+ def self.update_entry_by_json_pointer(hash, pointer, value, symbolize_keys: false)
46
+ return hash if hash.nil? || pointer.nil? || pointer.strip.empty?
47
+
48
+ begin
49
+ value_extractor = JsonPointer.new(hash, pointer, symbolize_keys: symbolize_keys)
50
+ value_extractor.value = value
51
+ hash
52
+ rescue StandardError
53
+ # Optionally log error or re-raise specific known ones
54
+ hash
107
55
  end
108
56
  end
109
-
110
- def path_fragments
111
- @path_fragments ||= @path.sub(%r{\A/}, '').split('/').map { |fragment| unescape_fragment(fragment) }
112
- end
113
-
114
- def escape_fragment(fragment)
115
- JsonPointerHelper.escape_fragment(fragment)
116
- end
117
-
118
- def unescape_fragment(fragment)
119
- JsonPointerHelper.unescape_fragment(fragment)
120
- end
121
-
122
- def fragment_to_key(fragment)
123
- if @options[:symbolize_keys]
124
- fragment.to_sym
125
- else
126
- fragment
127
- end
128
- end
129
-
130
- def fragment_to_index(fragment)
131
- fragment.to_i
132
- end
133
57
  end
134
58
  end
data/lib/apimatic_core.rb CHANGED
@@ -22,10 +22,18 @@ require_relative 'apimatic-core/logger/nil_sdk_logger'
22
22
  require_relative 'apimatic-core/logger/sdk_logger'
23
23
  require_relative 'apimatic-core/logger/configurations/api_logging_configuration'
24
24
 
25
+ require_relative 'apimatic-core/pagination/paginated_data'
26
+ require_relative 'apimatic-core/pagination/pagination_strategy'
27
+ require_relative 'apimatic-core/pagination/strategies/cursor_pagination'
28
+ require_relative 'apimatic-core/pagination/strategies/link_pagination'
29
+ require_relative 'apimatic-core/pagination/strategies/offset_pagination'
30
+ require_relative 'apimatic-core/pagination/strategies/page_pagination'
31
+
25
32
  require_relative 'apimatic-core/http/configurations/http_client_configuration'
26
33
  require_relative 'apimatic-core/http/request/http_request'
27
34
  require_relative 'apimatic-core/http/response/http_response'
28
35
  require_relative 'apimatic-core/http/response/api_response'
36
+ require_relative 'apimatic-core/http/http_call_context'
29
37
 
30
38
  require_relative 'apimatic-core/types/parameter'
31
39
  require_relative 'apimatic-core/types/error_case'
@@ -50,6 +58,8 @@ require_relative 'apimatic-core/utilities/union_type_helper'
50
58
  require_relative 'apimatic-core/utilities/json_pointer_helper'
51
59
  require_relative 'apimatic-core/utilities/logger_helper'
52
60
  require_relative 'apimatic-core/utilities/constants'
61
+ require_relative 'apimatic-core/utilities/deep_clone_utils'
62
+ require_relative 'apimatic-core/utilities/json_pointer'
53
63
 
54
64
  require_relative 'apimatic-core/authentication/header_auth'
55
65
  require_relative 'apimatic-core/authentication/query_auth'
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.3.15
4
+ version: 0.3.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - APIMatic Ltd.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-08 00:00:00.000000000 Z
11
+ date: 2025-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apimatic_core_interfaces
@@ -170,6 +170,7 @@ files:
170
170
  - lib/apimatic-core/exceptions/one_of_validation_exception.rb
171
171
  - lib/apimatic-core/factories/http_response_factory.rb
172
172
  - lib/apimatic-core/http/configurations/http_client_configuration.rb
173
+ - lib/apimatic-core/http/http_call_context.rb
173
174
  - lib/apimatic-core/http/request/http_request.rb
174
175
  - lib/apimatic-core/http/response/api_response.rb
175
176
  - lib/apimatic-core/http/response/http_response.rb
@@ -177,6 +178,12 @@ files:
177
178
  - lib/apimatic-core/logger/default_logger.rb
178
179
  - lib/apimatic-core/logger/nil_sdk_logger.rb
179
180
  - lib/apimatic-core/logger/sdk_logger.rb
181
+ - lib/apimatic-core/pagination/paginated_data.rb
182
+ - lib/apimatic-core/pagination/pagination_strategy.rb
183
+ - lib/apimatic-core/pagination/strategies/cursor_pagination.rb
184
+ - lib/apimatic-core/pagination/strategies/link_pagination.rb
185
+ - lib/apimatic-core/pagination/strategies/offset_pagination.rb
186
+ - lib/apimatic-core/pagination/strategies/page_pagination.rb
180
187
  - lib/apimatic-core/request_builder.rb
181
188
  - lib/apimatic-core/response_handler.rb
182
189
  - lib/apimatic-core/types/error_case.rb
@@ -195,7 +202,9 @@ files:
195
202
  - lib/apimatic-core/utilities/comparison_helper.rb
196
203
  - lib/apimatic-core/utilities/constants.rb
197
204
  - lib/apimatic-core/utilities/date_time_helper.rb
205
+ - lib/apimatic-core/utilities/deep_clone_utils.rb
198
206
  - lib/apimatic-core/utilities/file_helper.rb
207
+ - lib/apimatic-core/utilities/json_pointer.rb
199
208
  - lib/apimatic-core/utilities/json_pointer_helper.rb
200
209
  - lib/apimatic-core/utilities/logger_helper.rb
201
210
  - lib/apimatic-core/utilities/union_type_helper.rb