apimatic_core 0.3.14 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00e9fe70fd66ace8622a12149e08ae651839bf8c3ab555fd88b48075b63fa3f3
4
- data.tar.gz: 47b65583f2a0f4badb201965ea172c7683bdba5074e30d737ca2f35a04605b7f
3
+ metadata.gz: 6ecc4f4597c759974681f7105595172534b8ae7864654711e4f7ed04db6fa518
4
+ data.tar.gz: d9d62ba896f853026f2d52f242241048a91ae5e49a1b5906b2c1ee78ee3559e3
5
5
  SHA512:
6
- metadata.gz: 19bf7dbb3489e33e7d9aa84f97fc28a98e9360e9c0a631750844041c46348c9fed64e3060dd33c381fd5eedcc53386a142970c5a821c0d9119f7fbe250d7d93f
7
- data.tar.gz: 6c93a64bb2fb27f72688fea390a0f97f47729e945d75bf6c2df19f6708f13bd9a832cc4785129edb4310fce6f995012ecc8d80724fc1022f37b80055900f7384
6
+ metadata.gz: 3df51d480ddfc862d60c5452f6b15a71d091ebe64db5e5f86ef885ee402f9dffabee2a644a94c33a6572ce55383b04cf8720b4d70f9d87fece77da65741c9626
7
+ data.tar.gz: 7eda9192920928a35a9d9e09bf2937348972aa8bcdaf37fdd153da1abf357b9c259cee74e59c17e1845cbb2fb57cbcd18997dd5e5c381422d0e74ac692249368
data/README.md CHANGED
@@ -62,22 +62,32 @@ gem 'apimatic_core'
62
62
  | [`HttpResponseFactory`](lib/apimatic-core/factories/http_response_factory.rb) | Factory class to create an HTTP Response |
63
63
 
64
64
  ## HTTP
65
- | Name | Description |
66
- |-------------------------------------------------------------------------------------------------|----------------------------------------------------------|
67
- | [`HttpClientConfiguration`](lib/apimatic-core/http/configurations/http_client_configuration.rb) | Class used for configuring SDK by a user |
68
- | [`HttpRequest`](lib/apimatic-core/http/request/http_request.rb) | Class which contains information about the HTTP Request |
69
- | [`ApiResponse`](lib/apimatic-core/http/response/api_response.rb) | Wrapper class for Api Response |
70
- | [`HttpResponse`](lib/apimatic-core/http/response/http_response.rb) | Class which contains information about the HTTP Response |
65
+ | Name | Description |
66
+ |-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
67
+ | [`HttpClientConfiguration`](lib/apimatic-core/http/configurations/http_client_configuration.rb) | Class used for configuring SDK by a user |
68
+ | [`HttpRequest`](lib/apimatic-core/http/request/http_request.rb) | Class which contains information about the HTTP Request |
69
+ | [`ApiResponse`](lib/apimatic-core/http/response/api_response.rb) | Wrapper class for Api Response |
70
+ | [`HttpResponse`](lib/apimatic-core/http/response/http_response.rb) | Class which contains information about the HTTP Response |
71
+ | [`HttpCallContext`](lib/apimatic-core/http/http_call_context.rb) | This class captures the HTTP request and response lifecycle during an API call and is used with clients or controllers that support pre- and post-request hooks. |
71
72
 
72
73
  ## Logger
73
- | Name | Description |
74
- |-------------------------------------------------------------------|--------------------------------------------|
75
- | [`SdkLogger`](lib/apimatic-core/logger/sdk_logger.rb) | A class responsible for logging request and response of an api call|
76
- | [`NilSdkLogger`](lib/apimatic-core/logger/nil_sdk_logger.rb) | A class responsible for no logging|
77
- | [`ConsoleLogger`](lib/apimatic-core/logger/default_logger.rb) | Represents default implementation of logger interface|
78
- | [`ApiLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb) | Represents logging configuration|
79
- | [`ApiRequestLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb) | Represents request logging configuration.|
80
- | [`ApiResponseLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb) | Represents response logging configuration.|
74
+ | Name | Description |
75
+ |--------------------------------------------------------------------------------------------|---------------------------------------------------------------------|
76
+ | [`SdkLogger`](lib/apimatic-core/logger/sdk_logger.rb) | A class responsible for logging request and response of an api call |
77
+ | [`NilSdkLogger`](lib/apimatic-core/logger/nil_sdk_logger.rb) | A class responsible for no logging |
78
+ | [`ConsoleLogger`](lib/apimatic-core/logger/default_logger.rb) | Represents default implementation of logger interface |
79
+ | [`ApiLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb) | Represents logging configuration |
80
+ | [`ApiRequestLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb) | Represents request logging configuration. |
81
+ | [`ApiResponseLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb) | Represents response logging configuration. |
82
+
83
+ ## Pagination
84
+ | Name | Description |
85
+ |------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
86
+ | [`CursorPagination`](lib/apimatic-core/pagination/strategies/cursor_pagination.rb) | Cursor-based pagination strategy that handles extraction and injection of cursor values for seamless traversal across paged API responses. |
87
+ | [`LinkPagination`](lib/apimatic-core/pagination/strategies/link_pagination.rb) | Extracts the next page link from API responses via a JSON pointer and updates the request builder with corresponding query parameters. |
88
+ | [`OffsetPagination`](lib/apimatic-core/pagination/strategies/offset_pagination.rb) | Offset-based pagination using a configurable JSON pointer to update and track offset values in the request builder across responses. |
89
+ | [`PagePagination`](lib/apimatic-core/pagination/strategies/page_pagination.rb) | Page-based pagination strategy that updates the request builder with page numbers using a JSON pointer and wraps each response with metadata. |
90
+ | [`PaginatedData`](lib/apimatic-core/pagination/paginated_data.rb) | Iterator for paginated API responses supporting multiple strategies, item/page iteration, and access to the latest response and request builder. |
81
91
 
82
92
  ## Types
83
93
  | Name | Description |
@@ -94,14 +104,19 @@ gem 'apimatic_core'
94
104
  | [`XmlAttributes`](lib/apimatic-core/types/xml_attributes.rb) | A class to represent information about an XML Parameter passed in an endpoint |
95
105
 
96
106
  ## Utilities
97
- | Name | Description |
98
- |------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
99
- | [`ApiHelper`](lib/apimatic-core/utilities/api_helper.rb) | A Helper Class with various functions associated with making an API Call |
100
- | [`AuthHelper`](lib/apimatic-core/utilities/auth_helper.rb) | A Helper Class with various functions associated with authentication in API Calls |
101
- | [`UnionTypeHelper`](lib/apimatic-core/utilities/union_type_helper.rb) | A Helper Class with various functions associated with Union type in API Calls |
102
- | [`ComparisonHelper`](lib/apimatic-core/utilities/comparison_helper.rb) | A Helper Class used for the comparison of expected and actual API response |
103
- | [`FileHelper`](lib/apimatic-core/utilities/file_helper.rb) | A Helper Class for files |
104
- | [`XmlHelper`](lib/apimatic-core/utilities/xml_helper.rb ) | A Helper class that holds utility methods for xml serialization and deserialization. |
107
+ | Name | Description |
108
+ |----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
109
+ | [`ApiHelper`](lib/apimatic-core/utilities/api_helper.rb) | A Helper Class with various functions associated with making an API Call |
110
+ | [`AuthHelper`](lib/apimatic-core/utilities/auth_helper.rb) | A Helper Class with various functions associated with authentication in API Calls |
111
+ | [`UnionTypeHelper`](lib/apimatic-core/utilities/union_type_helper.rb) | A Helper Class with various functions associated with Union type in API Calls |
112
+ | [`ComparisonHelper`](lib/apimatic-core/utilities/comparison_helper.rb) | A Helper Class used for the comparison of expected and actual API response |
113
+ | [`FileHelper`](lib/apimatic-core/utilities/file_helper.rb) | A Helper Class for files |
114
+ | [`XmlHelper`](lib/apimatic-core/utilities/xml_helper.rb ) | A Helper class that holds utility methods for xml serialization and deserialization. |
115
+ | [`DateTimeHelper`](lib/apimatic-core/utilities/date_time_helper.rb ) | Utility methods for date-time format conversions. |
116
+ | [`DeepCloneUtils`](lib/apimatic-core/utilities/deep_clone_utils.rb ) | Utility methods for deep cloning arrays, hashes, and objects. |
117
+ | [`JsonPointerHelper`](lib/apimatic-core/utilities/json_pointer_helper.rb ) | Utility methods for getting and setting JSON pointer values in hashes. |
118
+ | [`JsonPointer`](lib/apimatic-core/utilities/json_pointer.rb ) | Enables querying, updating, and deleting values in nested Ruby Hashes and Arrays using JSON Pointer syntax (RFC 6901). |
119
+ | [`LoggerHelper`](lib/apimatic-core/utilities/logger_helper.rb ) | Utility methods for logging. |
105
120
 
106
121
  ## Links
107
122
  * [apimatic_core_interfaces](https://rubygems.org/gems/apimatic_core_interfaces)
@@ -1,11 +1,7 @@
1
1
  module CoreLibrary
2
2
  # This class is responsible for executing an API call using HttpClient, RequestBuilder and ResponseHandler objects.
3
3
  class ApiCall
4
- # Creates a new builder instance of the API call with pre-configured global and logging configurations.
5
- # @return [ApiCall] The instance of ApiCall object.
6
- def new_builder
7
- ApiCall.new(@global_configuration)
8
- end
4
+ attr_reader :request_builder, :pagination_strategy_list, :global_configuration
9
5
 
10
6
  # Initializes a new instance of ApiCall.
11
7
  # @param [GlobalConfiguration] global_configuration An instance of GlobalConfiguration.
@@ -15,6 +11,7 @@ module CoreLibrary
15
11
  @response_handler = ResponseHandler.new
16
12
  @endpoint_context = {}
17
13
  initialize_api_logger(@global_configuration.client_configuration.logging_configuration)
14
+ @pagination_strategy_list = nil
18
15
  end
19
16
 
20
17
  # The setter for the request builder to be used for building the request of an API call.
@@ -42,6 +39,15 @@ module CoreLibrary
42
39
  self
43
40
  end
44
41
 
42
+ # Sets the pagination strategies for this instance.
43
+ #
44
+ # @param pagination_strategies [Array<Object>] A variable number of pagination strategy objects.
45
+ # @return [self] Returns the current instance for method chaining.
46
+ def pagination_strategies(*pagination_strategies)
47
+ @pagination_strategy_list = pagination_strategies
48
+ self
49
+ end
50
+
45
51
  # Executes the API call using provided HTTP client, request builder and response handler objects.
46
52
  # @return The deserialized endpoint response.
47
53
  def execute
@@ -78,6 +84,17 @@ module CoreLibrary
78
84
  end
79
85
  end
80
86
 
87
+ # Invokes the given page iterable creator with a PaginatedData instance.
88
+ #
89
+ # @param page_iterable_creator [Proc] A callable that accepts a PaginatedData instance
90
+ # and returns an iterable (e.g., Enumerator) over pages of results.
91
+ # @param paginated_items_converter [Proc] A callable used to convert paginated items.
92
+ #
93
+ # @return [Object] The result of calling page_iterable_creator with the PaginatedData.
94
+ def paginate(page_iterable_creator, paginated_items_converter)
95
+ page_iterable_creator.call(PaginatedData.new(self, paginated_items_converter))
96
+ end
97
+
81
98
  # Registers request and response with the provided http_callback
82
99
  # @param [Callable] callable The callable to be called for registering into the HttpCallback instance.
83
100
  def update_http_callback(callable)
@@ -91,5 +108,37 @@ module CoreLibrary
91
108
  SdkLogger.new(logging_config)
92
109
  end
93
110
  end
111
+
112
+ # Creates a deep clone of the ApiCall instance with optional overrides.
113
+ #
114
+ # This method is useful for duplicating an API call while changing select parts like
115
+ # the request builder or pagination strategies.
116
+ #
117
+ # @param global_configuration [GlobalConfiguration, nil] Optional replacement global configuration.
118
+ # @param request_builder [RequestBuilder, nil] Optional replacement request builder.
119
+ #
120
+ # @return [ApiCall] A new instance with copied internal state and any applied overrides.
121
+ def clone_with(global_configuration: nil, request_builder: nil)
122
+ clone = ApiCall.new(global_configuration || @global_configuration)
123
+
124
+ clone.request(request_builder || @request_builder.clone_with)
125
+ clone.response(@response_handler)
126
+ clone.set_endpoint_context(DeepCloneUtils.deep_copy(@endpoint_context))
127
+ clone.pagination_strategies(*pagination_strategy_list) if pagination_strategy_list
128
+
129
+ clone
130
+ end
131
+
132
+ protected
133
+
134
+ # Sets the entire endpoint context hash to be used in building the API request.
135
+ #
136
+ # This replaces any previously set context values with the provided hash.
137
+ # It is typically used when cloning `ApiCall` instance.
138
+ #
139
+ # @param [Hash{String=>Object}] endpoint_context The full endpoint context to assign.
140
+ def set_endpoint_context(endpoint_context)
141
+ @endpoint_context = endpoint_context
142
+ end
94
143
  end
95
144
  end
@@ -136,5 +136,21 @@ module CoreLibrary
136
136
  end
137
137
  @global_headers['user-agent'] = user_agent unless user_agent.nil?
138
138
  end
139
+
140
+ def clone_with(client_configuration: nil)
141
+ clone = GlobalConfiguration.new(
142
+ client_configuration: client_configuration || DeepCloneUtils.deep_copy(@client_configuration)
143
+ )
144
+
145
+ # Copy internal state
146
+ clone.global_errors(DeepCloneUtils.deep_copy(@global_errors))
147
+ clone.global_headers(DeepCloneUtils.deep_copy(@global_headers))
148
+ clone.additional_headers(DeepCloneUtils.deep_copy(@additional_headers))
149
+ clone.auth_managers(DeepCloneUtils.deep_copy(@auth_managers))
150
+ clone.base_uri_executor(DeepCloneUtils.deep_copy(@base_uri_executor))
151
+ clone.symbolize_hash(@symbolize_hash)
152
+
153
+ clone
154
+ end
139
155
  end
140
156
  end
@@ -43,5 +43,23 @@ module CoreLibrary
43
43
  def set_http_client(http_client)
44
44
  @http_client = http_client
45
45
  end
46
+
47
+ def clone_with(http_callback: nil)
48
+ HttpClientConfiguration.new(
49
+ connection: DeepCloneUtils.deep_copy(@connection),
50
+ adapter: DeepCloneUtils.deep_copy(@adapter),
51
+ timeout: @timeout,
52
+ max_retries: @max_retries,
53
+ retry_interval: @retry_interval,
54
+ backoff_factor: @backoff_factor,
55
+ retry_statuses: DeepCloneUtils.deep_copy(@retry_statuses),
56
+ retry_methods: DeepCloneUtils.deep_copy(@retry_methods),
57
+ cache: @cache,
58
+ verify: @verify,
59
+ http_callback: http_callback || DeepCloneUtils.deep_copy(@http_callback),
60
+ http_client: DeepCloneUtils.deep_copy(@http_client),
61
+ logging_configuration: DeepCloneUtils.deep_copy(@logging_configuration)
62
+ )
63
+ end
46
64
  end
47
65
  end
@@ -0,0 +1,50 @@
1
+ module CoreLibrary
2
+ # HttpCallContext is a callback class used to capture the HTTP request and response
3
+ # lifecycle during an API call. It is intended to be passed to an HTTP client or controller
4
+ # that supports pre- and post-request hooks.
5
+ #
6
+ # This class stores references to the request and response objects, making them
7
+ # accessible after the API call is completed. This can be useful for debugging,
8
+ # logging, or validation purposes.
9
+ #
10
+ # Example usage:
11
+ # context = CoreLibrary::HttpCallContext.new
12
+ # client.execute_request(request, context)
13
+ # puts context.request # Inspect the HttpRequest
14
+ # puts context.response # Inspect the HttpResponse
15
+ class HttpCallContext < HttpCallback
16
+ # @return [HttpRequest, nil] The HTTP request object that was sent.
17
+ attr_reader :request
18
+
19
+ # @return [HttpResponse, nil] The HTTP response object that was received.
20
+ attr_reader :response
21
+
22
+ # Initializes a new instance of HttpCallContext.
23
+ #
24
+ # @param user_provided_http_callback [HttpCallback, nil] An optional user-defined callback
25
+ # that will be triggered before and after the HTTP request.
26
+ def initialize(user_provided_http_callback = nil)
27
+ @request = nil
28
+ @response = nil
29
+ @http_callback = user_provided_http_callback
30
+ end
31
+
32
+ # Called before making the HTTP request.
33
+ # Stores the request and invokes the user-provided callback, if any.
34
+ #
35
+ # @param request [HttpRequest] The request object to be sent to the HttpClient.
36
+ def on_before_request(request)
37
+ @request = request
38
+ @http_callback&.on_before_request(request)
39
+ end
40
+
41
+ # Called after receiving the HTTP response.
42
+ # Stores the response and invokes the user-provided callback, if any.
43
+ #
44
+ # @param response [HttpResponse] The HttpResponse of the API call.
45
+ def on_after_response(response)
46
+ @response = response
47
+ @http_callback&.on_after_response(response)
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,9 @@
1
1
  module CoreLibrary
2
2
  # Http response received.
3
3
  class HttpResponse
4
+ BODY_PARAM_POINTER = '$response.body'.freeze
5
+ HEADER_PARAM_POINTER = '$response.headers'.freeze
6
+
4
7
  attr_reader :status_code, :reason_phrase, :headers, :raw_body, :request
5
8
 
6
9
  # The constructor
@@ -20,5 +23,31 @@ module CoreLibrary
20
23
  @raw_body = raw_body
21
24
  @request = request
22
25
  end
26
+
27
+ # Resolves a JSON pointer against either the response body or response headers.
28
+ #
29
+ # This method is useful when extracting a specific value from an API response using a JSON pointer.
30
+ # It determines whether to extract from the body or headers based on the prefix in the pointer.
31
+ #
32
+ # @param json_pointer [String] A JSON pointer string (e.g., '/body/data/id' or '/headers/x-request-id').
33
+ # @return [Object, nil] The value located at the specified JSON pointer,
34
+ # or nil if not found or prefix is unrecognized.
35
+ def get_value_by_json_pointer(json_pointer)
36
+ param_pointer, field_pointer = JsonPointerHelper.split_into_parts(json_pointer)
37
+
38
+ value = case param_pointer
39
+ when HEADER_PARAM_POINTER
40
+ JsonPointerHelper.get_value_by_json_pointer(@headers, field_pointer)
41
+ when BODY_PARAM_POINTER
42
+ JsonPointerHelper.get_value_by_json_pointer(
43
+ ApiHelper.json_deserialize(@raw_body),
44
+ field_pointer
45
+ )
46
+ else
47
+ nil
48
+ end
49
+
50
+ value.nil? || (value.is_a? JsonPointer::NotFound) ? nil : value
51
+ end
23
52
  end
24
53
  end
@@ -0,0 +1,137 @@
1
+ module CoreLibrary
2
+ # Iterator class for handling paginated API responses.
3
+ #
4
+ # Provides methods to iterate over items and pages, fetch next pages using defined pagination strategies,
5
+ # and access the latest HTTP response and request builder.
6
+ class PaginatedData
7
+ include Enumerable
8
+
9
+ attr_reader :page_size
10
+
11
+ def initialize(api_call, paginated_items_converter)
12
+ raise ArgumentError, 'paginated_items_converter cannot be nil' if paginated_items_converter.nil?
13
+
14
+ @api_call = api_call
15
+ @paginated_items_converter = paginated_items_converter
16
+ @initial_request_builder = api_call.request_builder
17
+ @pagination_strategies = @api_call.pagination_strategy_list
18
+ @http_call_context = HttpCallContext.new(
19
+ @api_call.global_configuration.client_configuration.http_callback
20
+ )
21
+ http_client_config = @api_call.global_configuration.client_configuration.clone_with(
22
+ http_callback: @http_call_context
23
+ )
24
+ @global_configuration = @api_call.global_configuration.clone_with(
25
+ client_configuration: http_client_config
26
+ )
27
+
28
+ @last_request_builder = nil
29
+ @locked_strategy = nil
30
+ @page_size = 0
31
+ end
32
+
33
+ # Returns the most recent HTTP response received during pagination.
34
+ def last_response
35
+ @last_request_builder.nil? ? nil : @http_call_context.response
36
+ end
37
+
38
+ # Returns the appropriate request builder for the current pagination state.
39
+ def request_builder
40
+ @last_request_builder || @initial_request_builder
41
+ end
42
+
43
+ # Enables iteration over individual items.
44
+ def each
45
+ return enum_for(:each) unless block_given?
46
+
47
+ paginated_data = clone
48
+ current_index = 0
49
+ items = []
50
+
51
+ loop do
52
+ if current_index < paginated_data.page_size
53
+ yield items[current_index]
54
+ current_index += 1
55
+ else
56
+ response = paginated_data.fetch_next_page
57
+ break if response.nil?
58
+
59
+ items = @paginated_items_converter.call(response.data)
60
+ break if items.nil? || items.empty?
61
+
62
+ paginated_data.page_size = items.length
63
+ current_index = 0
64
+ end
65
+ end
66
+ end
67
+
68
+ # Yields each page of the paginated response.
69
+ def pages
70
+ Enumerator.new do |page|
71
+ paginated_data = clone
72
+
73
+ loop do
74
+ response = paginated_data.fetch_next_page
75
+ break if response.nil?
76
+
77
+ items = @paginated_items_converter.call(response.data)
78
+ break if items.nil? || items.empty?
79
+
80
+ paginated_data.page_size = items.length
81
+
82
+ page << response
83
+ end
84
+ end
85
+ end
86
+
87
+ # Returns a new independent PaginatedData instance.
88
+ def clone
89
+ cloned_api_call = @api_call.clone_with(request_builder: @initial_request_builder)
90
+ PaginatedData.new(cloned_api_call, @paginated_items_converter)
91
+ end
92
+
93
+ protected
94
+
95
+ attr_accessor :items, :current_index, :paged_response
96
+ attr_writer :page_size
97
+
98
+ # Fetches the next page using the appropriate pagination strategy.
99
+ def fetch_next_page
100
+ return execute_strategy(@locked_strategy) unless @locked_strategy.nil?
101
+
102
+ @pagination_strategies.each do |pagination_strategy|
103
+ response = execute_strategy(pagination_strategy)
104
+ next if response.nil?
105
+
106
+ @locked_strategy ||= get_locked_strategy
107
+ return response
108
+ end
109
+
110
+ nil
111
+ end
112
+
113
+ # Executes a pagination strategy: builds the request, performs the API call,
114
+ # and applies response metadata.
115
+ def execute_strategy(pagination_strategy)
116
+ _request_builder = pagination_strategy.apply(self)
117
+ return nil if _request_builder.nil?
118
+
119
+ @last_request_builder = _request_builder
120
+
121
+ response = @api_call.clone_with(
122
+ global_configuration: @global_configuration,
123
+ request_builder: _request_builder
124
+ ).execute
125
+
126
+ pagination_strategy.apply_metadata_wrapper(response)
127
+ end
128
+
129
+ # Finds and returns the first applicable pagination strategy
130
+ # based on the current response.
131
+ def get_locked_strategy
132
+ @pagination_strategies.find do |pagination_strategy|
133
+ pagination_strategy.applicable?(last_response)
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,46 @@
1
+ module CoreLibrary
2
+ # Abstract base class for implementing pagination strategies.
3
+ #
4
+ # Provides methods to initialize with pagination metadata, apply pagination logic to request builders,
5
+ # and update request builders with new pagination parameters based on JSON pointers.
6
+ class PaginationStrategy
7
+ attr_reader :metadata_wrapper
8
+
9
+ # Initializes the PaginationStrategy with the provided metadata wrapper.
10
+ #
11
+ # @param metadata_wrapper [Object] An object containing pagination metadata. Must not be nil.
12
+ # @raise [ArgumentError] If metadata_wrapper is nil.
13
+ def initialize(metadata_wrapper)
14
+ raise ArgumentError, 'Metadata wrapper for the pagination cannot be nil' if metadata_wrapper.nil?
15
+
16
+ @metadata_wrapper = metadata_wrapper
17
+ end
18
+
19
+ # Checks whether the pagination strategy is a valid candidate based on the given HTTP response.
20
+ #
21
+ # @param response [HttpResponse] The response data from the previous API call.
22
+ # @return [boolean] True if this strategy is valid based on the given HTTP response..
23
+ # @raise [NotImplementedError] This method must be implemented in a subclass.
24
+ def applicable?(response)
25
+ raise NotImplementedError, 'Subclasses must implement #is_applicable'
26
+ end
27
+
28
+ # Modifies the request builder to fetch the next page of results based on the provided paginated data.
29
+ #
30
+ # @param paginated_data [Object] The response data from the previous API call.
31
+ # @return [Object] An updated request builder configured for the next page request.
32
+ # @raise [NotImplementedError] This method must be implemented in a subclass.
33
+ def apply(paginated_data)
34
+ raise NotImplementedError, 'Subclasses must implement #apply'
35
+ end
36
+
37
+ # Processes the paged API response using the metadata wrapper.
38
+ #
39
+ # @param paged_response [Object] The response object containing paginated data.
40
+ # @return [Object] The processed response with applied pagination metadata.
41
+ # @raise [NotImplementedError] This method must be implemented in a subclass.
42
+ def apply_metadata_wrapper(paged_response)
43
+ raise NotImplementedError, 'Subclasses must implement #apply_metadata_wrapper'
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ module CoreLibrary
2
+ # Implements a cursor-based pagination strategy for API responses.
3
+ #
4
+ # This class manages the extraction and injection of cursor values between API requests and responses,
5
+ # enabling seamless traversal of paginated data.
6
+ class CursorPagination < PaginationStrategy
7
+ # Initializes a CursorPagination instance with the specified output and input pointers and a metadata wrapper.
8
+ #
9
+ # Validates that both input and output pointers are provided.
10
+ #
11
+ # @param output [String] JSON pointer to extract the cursor from the API response.
12
+ # @param input [String] JSON pointer indicating where to set the cursor in the request.
13
+ # @param metadata_wrapper [Proc] A callable to wrap paged responses with additional metadata.
14
+ # @raise [ArgumentError] If either input or output is nil.
15
+ def initialize(output, input, metadata_wrapper)
16
+ super metadata_wrapper
17
+
18
+ raise ArgumentError, 'Input pointer for cursor based pagination cannot be nil' if input.nil?
19
+ raise ArgumentError, 'Output pointer for cursor based pagination cannot be nil' if output.nil?
20
+
21
+ @output = output
22
+ @input = input
23
+ @cursor_value = nil
24
+ end
25
+
26
+ # Determines whether the cursor pagination strategy is applicable
27
+ # based on the given HTTP response.
28
+ #
29
+ # @param [HttpResponse, nil] response The response from the previous API call.
30
+ # @return [Boolean] true if this strategy is applicable based on the response; false otherwise.
31
+ def applicable?(response)
32
+ return true if response.nil?
33
+
34
+ @cursor_value = response.get_value_by_json_pointer(@output)
35
+
36
+ !@cursor_value.nil?
37
+ end
38
+
39
+ # Advances the pagination by updating the request builder with the next cursor value.
40
+ #
41
+ # If there is no previous response, initializes the cursor from the request builder.
42
+ # Otherwise, extracts the cursor from the last response using the configured output pointer.
43
+ #
44
+ # @param paginated_data [Object] An object containing the last response and request builder.
45
+ # @return [Object, nil] A new request builder for the next page, or nil if pagination is complete.
46
+ def apply(paginated_data)
47
+ last_response = paginated_data.last_response
48
+ request_builder = paginated_data.request_builder
49
+
50
+ # If there is no response yet, this is the first page
51
+ if last_response.nil?
52
+ @cursor_value = request_builder.get_parameter_value_by_json_pointer(@input).to_s
53
+
54
+ return request_builder
55
+ end
56
+
57
+ @cursor_value = last_response.get_value_by_json_pointer(@output)
58
+
59
+ return nil if @cursor_value.nil?
60
+
61
+ request_builder.get_updated_request_by_json_pointer(@input, @cursor_value)
62
+ end
63
+
64
+ # Applies the configured metadata wrapper to the paged response, including the current cursor value.
65
+ #
66
+ # @param paged_response [Object] The response object from the current page.
67
+ # @return [Object] The result of the metadata wrapper applied to the paged response and cursor value.
68
+ def apply_metadata_wrapper(paged_response)
69
+ @metadata_wrapper.call(paged_response, @cursor_value)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,67 @@
1
+ module CoreLibrary
2
+ # Implements a pagination strategy that extracts the next page link from API responses using a JSON pointer.
3
+ #
4
+ # This class updates the request builder with query parameters from the next page link
5
+ # and applies a metadata wrapper to the paged response.
6
+ class LinkPagination < PaginationStrategy
7
+ # Initializes a LinkPagination instance with the given next link pointer and metadata wrapper.
8
+ #
9
+ # @param next_link_pointer [String] JSON pointer to extract the next page link from the API response.
10
+ # @param metadata_wrapper [Proc] A callable to wrap the paged response metadata.
11
+ # @raise [ArgumentError] If next_link_pointer is nil.
12
+ def initialize(next_link_pointer, metadata_wrapper)
13
+ super(metadata_wrapper)
14
+
15
+ raise ArgumentError, 'Next link pointer for cursor based pagination cannot be nil' if next_link_pointer.nil?
16
+
17
+ @next_link_pointer = next_link_pointer
18
+ @next_link = nil
19
+ end
20
+
21
+ # Determines whether the link pagination strategy is applicable
22
+ # based on the given HTTP response.
23
+ #
24
+ # @param [HttpResponse, nil] response The response from the previous API call.
25
+ # @return [Boolean] true if this strategy is applicable based on the response; false otherwise.
26
+ def applicable?(response)
27
+ return true if response.nil?
28
+
29
+ @next_link = response.get_value_by_json_pointer(@next_link_pointer)
30
+
31
+ !@next_link.nil?
32
+ end
33
+
34
+ # Updates the request builder with query parameters from the next page link extracted from the last API response.
35
+ #
36
+ # @param paginated_data [Object] An object containing the last API response and the current request builder.
37
+ # @return [Object, nil] A new request builder with updated query parameters, or nil if no next link is found.
38
+ def apply(paginated_data)
39
+ last_response = paginated_data.last_response
40
+ request_builder = paginated_data.request_builder
41
+
42
+ # If there is no response yet, this is the first page
43
+ if last_response.nil?
44
+ @next_link = nil
45
+ return request_builder
46
+ end
47
+
48
+ @next_link = last_response.get_value_by_json_pointer(@next_link_pointer)
49
+
50
+ return nil if @next_link.nil?
51
+
52
+ query_params = ApiHelper.get_query_parameters(@next_link)
53
+ updated_query_params = DeepCloneUtils.deep_copy(request_builder.query_params)
54
+ updated_query_params.merge!(query_params)
55
+
56
+ request_builder.clone_with(query_params: updated_query_params)
57
+ end
58
+
59
+ # Applies the metadata wrapper to the paged response, including the next page link.
60
+ #
61
+ # @param paged_response [Object] The API response object for the current page.
62
+ # @return [Object] The result of the metadata wrapper, typically containing the response and next link.
63
+ def apply_metadata_wrapper(paged_response)
64
+ @metadata_wrapper.call(paged_response, @next_link)
65
+ end
66
+ end
67
+ end