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 +4 -4
- data/README.md +37 -22
- data/lib/apimatic-core/api_call.rb +54 -5
- data/lib/apimatic-core/configurations/global_configuration.rb +16 -0
- data/lib/apimatic-core/http/configurations/http_client_configuration.rb +18 -0
- data/lib/apimatic-core/http/http_call_context.rb +50 -0
- data/lib/apimatic-core/http/response/http_response.rb +29 -0
- data/lib/apimatic-core/pagination/paginated_data.rb +137 -0
- data/lib/apimatic-core/pagination/pagination_strategy.rb +46 -0
- data/lib/apimatic-core/pagination/strategies/cursor_pagination.rb +72 -0
- data/lib/apimatic-core/pagination/strategies/link_pagination.rb +67 -0
- data/lib/apimatic-core/pagination/strategies/offset_pagination.rb +63 -0
- data/lib/apimatic-core/pagination/strategies/page_pagination.rb +60 -0
- data/lib/apimatic-core/request_builder.rb +134 -13
- data/lib/apimatic-core/response_handler.rb +2 -2
- data/lib/apimatic-core/types/parameter.rb +2 -2
- data/lib/apimatic-core/utilities/api_helper.rb +39 -1
- data/lib/apimatic-core/utilities/deep_clone_utils.rb +114 -0
- data/lib/apimatic-core/utilities/file_helper.rb +6 -6
- data/lib/apimatic-core/utilities/json_pointer.rb +257 -0
- data/lib/apimatic-core/utilities/json_pointer_helper.rb +48 -124
- data/lib/apimatic_core.rb +10 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ecc4f4597c759974681f7105595172534b8ae7864654711e4f7ed04db6fa518
|
4
|
+
data.tar.gz: d9d62ba896f853026f2d52f242241048a91ae5e49a1b5906b2c1ee78ee3559e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
74
|
-
|
75
|
-
| [`SdkLogger`](lib/apimatic-core/logger/sdk_logger.rb)
|
76
|
-
| [`NilSdkLogger`](lib/apimatic-core/logger/nil_sdk_logger.rb)
|
77
|
-
| [`ConsoleLogger`](lib/apimatic-core/logger/default_logger.rb)
|
78
|
-
| [`ApiLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb)
|
79
|
-
| [`ApiRequestLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb)
|
80
|
-
| [`ApiResponseLoggingConfiguration`](lib/apimatic-core/logger/api_logging_configuration.rb)
|
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
|
98
|
-
|
99
|
-
| [`ApiHelper`](lib/apimatic-core/utilities/api_helper.rb)
|
100
|
-
| [`AuthHelper`](lib/apimatic-core/utilities/auth_helper.rb)
|
101
|
-
| [`UnionTypeHelper`](lib/apimatic-core/utilities/union_type_helper.rb)
|
102
|
-
| [`ComparisonHelper`](lib/apimatic-core/utilities/comparison_helper.rb)
|
103
|
-
| [`FileHelper`](lib/apimatic-core/utilities/file_helper.rb)
|
104
|
-
| [`XmlHelper`](lib/apimatic-core/utilities/xml_helper.rb )
|
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
|
-
|
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
|