Skip to content

Commit 98aee2c

Browse files
klaviyo-sdkklaviyo-sdkben-horgan-klaviyo
authored
v11.0.0 (#32)
* version 11.0.0 * Update CHANGELOG.md --------- Co-authored-by: klaviyo-sdk <[email protected]> Co-authored-by: Ben Horgan <[email protected]>
1 parent a61cac7 commit 98aee2c

File tree

9 files changed

+93
-52
lines changed

9 files changed

+93
-52
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [11.0.0] - revision 2024-10-15
8+
### Changed
9+
- Improved Retry Logic
10+
- To address 429 status codes, we have modified the retry logic to use exponential backoff and the `Retry-After` header.
11+
- **Breaking:** Retry logic is now enabled by default. To disable it, set `config.max_retries` to `0`.
12+
713
## [10.0.0] - 2024-10-15
814
### Added
915
- Universal Content API

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Klaviyo Ruby SDK
22

3-
- SDK version: 10.0.0
3+
- SDK version: 11.0.0
44
- API revision: 2024-10-15
55

66
## Helpful Resources
@@ -110,13 +110,13 @@ gem build klaviyo-api-sdk.gemspec
110110
Then install the gem locally:
111111

112112
```shell
113-
gem install ./klaviyo-api-sdk-10.0.0.gem
113+
gem install ./klaviyo-api-sdk-11.0.0.gem
114114
```
115115

116116

117117
Finally add this to the Gemfile:
118118

119-
gem 'klaviyo-api-sdk', '~> 10.0.0'
119+
gem 'klaviyo-api-sdk', '~> 11.0.0'
120120

121121
To install directly from rubygems:
122122

@@ -152,8 +152,10 @@ end
152152

153153
NOTE:
154154
* The SDK retries on resolvable errors, namely: rate limits (common) and server errors on klaviyo (rare).
155-
* `max_retry` denotes number of attempts the client will make in order to execute the request successfully.
156-
* `max_delay` denotes total delay (in seconds) across all attempts.
155+
* `max_retries` denotes the number of attempts the client will make in order to execute the request successfully.
156+
* Set to 0 to disable automatic retries
157+
* `max_delay` denotes the maximum amount of time (in seconds) that will be spent retrying a request across all attempts.
158+
* The time interval between retries is calculated using exponential backoff and the `Retry-After` header.
157159

158160
### To call the `get_catalog_items` operation:
159161

@@ -2619,11 +2621,11 @@ KlaviyoAPI::Profiles.create_profile_suppression_bulk_delete_job(body)
26192621
#### [Create or Update Profile](https://developers.klaviyo.com/en/v2024-10-15/reference/create_or_update_profile)
26202622

26212623
```ruby
2622-
KlaviyoAPI::Profiles.create_or_update_profile(body)
2624+
KlaviyoAPI::Profiles.create_or_update_profile(body, opts)
26232625
```
26242626
##### Method alias:
26252627
```ruby
2626-
KlaviyoAPI::Profiles.create_profile_import(body)
2628+
KlaviyoAPI::Profiles.create_profile_import(body, opts)
26272629
```
26282630

26292631

@@ -2633,7 +2635,7 @@ KlaviyoAPI::Profiles.create_profile_import(body)
26332635
#### [Create Profile](https://developers.klaviyo.com/en/v2024-10-15/reference/create_profile)
26342636

26352637
```ruby
2636-
KlaviyoAPI::Profiles.create_profile(body)
2638+
KlaviyoAPI::Profiles.create_profile(body, opts)
26372639
```
26382640

26392641

@@ -2943,7 +2945,7 @@ KlaviyoAPI::Profiles.create_profile_bulk_import_job(body)
29432945
#### [Update Profile](https://developers.klaviyo.com/en/v2024-10-15/reference/update_profile)
29442946

29452947
```ruby
2946-
KlaviyoAPI::Profiles.update_profile(id, body)
2948+
KlaviyoAPI::Profiles.update_profile(id, body, opts)
29472949
```
29482950

29492951

klaviyo-api-sdk.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ require "klaviyo-api-sdk/version"
1717

1818
Gem::Specification.new do |s|
1919
s.name = "klaviyo-api-sdk"
20-
s.version = "10.0.0"
20+
s.version = "11.0.0"
2121
s.authors = ['Klaviyo Team']
2222
s.email = ['[email protected]']
2323
s.summary = 'You heard us, a Ruby wrapper for the Klaviyo API'

lib/klaviyo-api-sdk.rb

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -660,25 +660,10 @@ def configure
660660
# recreate methods
661661
original_class.public_instance_methods(false).each do |m|
662662
wrapper_class.class_eval {
663-
define_singleton_method m do |*arg|
664-
# max_delay=60, max_retries=3
665-
# retry_codes = [429,503,504]
666-
# only add retriable if both of these are not set
667-
max_retries = Configuration.default.max_retries
668-
max_delay = Configuration.default.max_delay
669-
670-
if (max_retries != nil && max_delay != nil)
671-
Retriable.configure do |c|
672-
c.tries = max_retries
673-
c.max_elapsed_time = max_delay
674-
c.on = {
675-
KlaviyoAPI::ApiError => [/429/, /503/, /504/]
676-
}
677-
end
678-
Retriable.retriable do
679-
KlaviyoAPI.const_get(c).new.send(m, *arg)
680-
end
681-
else
663+
define_singleton_method m do |*arg|
664+
max_retries = Configuration.default.max_retries || 3
665+
max_delay = Configuration.default.max_delay || 60
666+
KlaviyoAPI.with_retry(max_retries, max_delay) do
682667
KlaviyoAPI.const_get(c).new.send(m, *arg)
683668
end
684669
end
@@ -689,5 +674,33 @@ def configure
689674
@is_initialized = true
690675
end
691676
end
677+
678+
def with_retry(tries, max_elapsed_time)
679+
start_time = Time.now
680+
elapsed_time = -> { Time.now - start_time }
681+
last_request_retry_after = nil
682+
last_request_timestamp = nil
683+
index = 0
684+
attempt = 0
685+
last_exception = nil
686+
while true
687+
begin
688+
retry_after_value_elapsed = last_request_retry_after == nil || Time.now - last_request_timestamp > Integer(last_request_retry_after)
689+
if retry_after_value_elapsed
690+
attempt += 1
691+
return yield
692+
end
693+
rescue KlaviyoAPI::ApiError => exception
694+
last_exception = exception
695+
last_request_retry_after = exception.response_headers[:'Retry-After']
696+
last_request_timestamp = Time.now
697+
raise unless [429, 503, 504, 524].include? exception.code
698+
end
699+
interval = Retriable::ExponentialBackoff.new(tries: index + 1).intervals[index]
700+
raise last_exception if attempt >= tries || (elapsed_time.call + interval) > max_elapsed_time
701+
sleep interval
702+
index += 1
703+
end
704+
end
692705
end
693706
end

lib/klaviyo-api-sdk/api/profiles_api.rb

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ def bulk_unsuppress_profiles_with_http_info(suppression_delete_job_create_query,
351351
# Given a set of profile attributes and optionally an ID, create or update a profile. Returns 201 if a new profile was created, 200 if an existing profile was updated. Note that setting a field to `null` will clear out the field, whereas not including a field in your request will leave it unchanged.<br><br>*Rate limits*:<br>Burst: `75/s`<br>Steady: `700/m` **Scopes:** `profiles:write`
352352
# @param profile_upsert_query [ProfileUpsertQuery]
353353
# @param [Hash] opts the optional parameters
354+
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
354355
# @return [Hash<String, Object>]
355356
def create_or_update_profile(profile_upsert_query, opts = {})
356357
data, _status_code, _headers = create_or_update_profile_with_http_info(profile_upsert_query, opts)
@@ -364,6 +365,7 @@ def create_or_update_profile(profile_upsert_query, opts = {})
364365
# Given a set of profile attributes and optionally an ID, create or update a profile. Returns 201 if a new profile was created, 200 if an existing profile was updated. Note that setting a field to &#x60;null&#x60; will clear out the field, whereas not including a field in your request will leave it unchanged.&lt;br&gt;&lt;br&gt;*Rate limits*:&lt;br&gt;Burst: &#x60;75/s&#x60;&lt;br&gt;Steady: &#x60;700/m&#x60; **Scopes:** &#x60;profiles:write&#x60;
365366
# @param profile_upsert_query [ProfileUpsertQuery]
366367
# @param [Hash] opts the optional parameters
368+
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
367369
# @return [Array<(Hash<String, Object>, Integer, Hash)>] Hash<String, Object> data, response status code and response headers
368370
def create_or_update_profile_with_http_info(profile_upsert_query, opts = {})
369371
if @api_client.config.debugging
@@ -373,11 +375,16 @@ def create_or_update_profile_with_http_info(profile_upsert_query, opts = {})
373375
if @api_client.config.client_side_validation && profile_upsert_query.nil?
374376
fail ArgumentError, "Missing the required parameter 'profile_upsert_query' when calling ProfilesApi.create_or_update_profile"
375377
end
378+
allowable_values = ["subscriptions", "predictive_analytics"]
379+
if @api_client.config.client_side_validation && opts[:'additional_fields_profile'] && !opts[:'additional_fields_profile'].all? { |item| allowable_values.include?(item) }
380+
fail ArgumentError, "invalid value for \"additional_fields_profile\", must include one of #{allowable_values}"
381+
end
376382
# resource path
377383
local_var_path = '/api/profile-import'
378384

379385
# query parameters
380386
query_params = opts[:query_params] || {}
387+
query_params[:'additional-fields[profile]'] = @api_client.build_collection_param(opts[:'additional_fields_profile'], :csv) if !opts[:'additional_fields_profile'].nil?
381388

382389
# header parameters
383390
header_params = opts[:header_params] || {}
@@ -427,6 +434,7 @@ def create_or_update_profile_with_http_info(profile_upsert_query, opts = {})
427434
# Create a new profile.<br><br>*Rate limits*:<br>Burst: `75/s`<br>Steady: `700/m` **Scopes:** `profiles:write`
428435
# @param profile_create_query [ProfileCreateQuery]
429436
# @param [Hash] opts the optional parameters
437+
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
430438
# @return [Hash<String, Object>]
431439
def create_profile(profile_create_query, opts = {})
432440
data, _status_code, _headers = create_profile_with_http_info(profile_create_query, opts)
@@ -437,6 +445,7 @@ def create_profile(profile_create_query, opts = {})
437445
# Create a new profile.&lt;br&gt;&lt;br&gt;*Rate limits*:&lt;br&gt;Burst: &#x60;75/s&#x60;&lt;br&gt;Steady: &#x60;700/m&#x60; **Scopes:** &#x60;profiles:write&#x60;
438446
# @param profile_create_query [ProfileCreateQuery]
439447
# @param [Hash] opts the optional parameters
448+
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
440449
# @return [Array<(Hash<String, Object>, Integer, Hash)>] Hash<String, Object> data, response status code and response headers
441450
def create_profile_with_http_info(profile_create_query, opts = {})
442451
if @api_client.config.debugging
@@ -446,11 +455,16 @@ def create_profile_with_http_info(profile_create_query, opts = {})
446455
if @api_client.config.client_side_validation && profile_create_query.nil?
447456
fail ArgumentError, "Missing the required parameter 'profile_create_query' when calling ProfilesApi.create_profile"
448457
end
458+
allowable_values = ["subscriptions", "predictive_analytics"]
459+
if @api_client.config.client_side_validation && opts[:'additional_fields_profile'] && !opts[:'additional_fields_profile'].all? { |item| allowable_values.include?(item) }
460+
fail ArgumentError, "invalid value for \"additional_fields_profile\", must include one of #{allowable_values}"
461+
end
449462
# resource path
450463
local_var_path = '/api/profiles'
451464

452465
# query parameters
453466
query_params = opts[:query_params] || {}
467+
query_params[:'additional-fields[profile]'] = @api_client.build_collection_param(opts[:'additional_fields_profile'], :csv) if !opts[:'additional_fields_profile'].nil?
454468

455469
# header parameters
456470
header_params = opts[:header_params] || {}
@@ -1498,7 +1512,7 @@ def get_lists_for_profile_with_http_info(id, opts = {})
14981512
alias get_profile_lists_with_http_info get_lists_for_profile_with_http_info
14991513

15001514
# Get Profile
1501-
# Get the profile with the given profile ID.<br><br>*Rate limits*:<br>Burst: `75/s`<br>Steady: `700/m` **Scopes:** `profiles:read`
1515+
# Get the profile with the given profile ID.<br><br>*Rate limits*:<br>Burst: `75/s`<br>Steady: `700/m`<br><br>Rate limits when using the `include=list` parameter in your API request:<br>Burst: `1/s`<br>Steady: `15/m`<br><br>Rate limits when using the `include=segment` parameter in your API request:<br>Burst: `1/s`<br>Steady: `15/m`<br><br>To learn more about how the `include` parameter impacts rate limits, check out our [Rate limits, status codes, and errors](https://developers.klaviyo.com/en/v2024-10-15/docs/rate_limits_and_error_handling) guide. **Scopes:** `profiles:read`
15021516
# @param id [String]
15031517
# @param [Hash] opts the optional parameters
15041518
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
@@ -1513,7 +1527,7 @@ def get_profile(id, opts = {})
15131527
end
15141528

15151529
# Get Profile
1516-
# Get the profile with the given profile ID.&lt;br&gt;&lt;br&gt;*Rate limits*:&lt;br&gt;Burst: &#x60;75/s&#x60;&lt;br&gt;Steady: &#x60;700/m&#x60; **Scopes:** &#x60;profiles:read&#x60;
1530+
# Get the profile with the given profile ID.&lt;br&gt;&lt;br&gt;*Rate limits*:&lt;br&gt;Burst: &#x60;75/s&#x60;&lt;br&gt;Steady: &#x60;700/m&#x60;&lt;br&gt;&lt;br&gt;Rate limits when using the &#x60;include&#x3D;list&#x60; parameter in your API request:&lt;br&gt;Burst: &#x60;1/s&#x60;&lt;br&gt;Steady: &#x60;15/m&#x60;&lt;br&gt;&lt;br&gt;Rate limits when using the &#x60;include&#x3D;segment&#x60; parameter in your API request:&lt;br&gt;Burst: &#x60;1/s&#x60;&lt;br&gt;Steady: &#x60;15/m&#x60;&lt;br&gt;&lt;br&gt;To learn more about how the &#x60;include&#x60; parameter impacts rate limits, check out our [Rate limits, status codes, and errors](https://developers.klaviyo.com/en/v2024-10-15/docs/rate_limits_and_error_handling) guide. **Scopes:** &#x60;profiles:read&#x60;
15171531
# @param id [String]
15181532
# @param [Hash] opts the optional parameters
15191533
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
@@ -2202,6 +2216,7 @@ def spawn_bulk_profile_import_job_with_http_info(profile_import_job_create_query
22022216
# @param id [String] Primary key that uniquely identifies this profile. Generated by Klaviyo.
22032217
# @param profile_partial_update_query [ProfilePartialUpdateQuery]
22042218
# @param [Hash] opts the optional parameters
2219+
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
22052220
# @return [Hash<String, Object>]
22062221
def update_profile(id, profile_partial_update_query, opts = {})
22072222
data, _status_code, _headers = update_profile_with_http_info(id, profile_partial_update_query, opts)
@@ -2213,6 +2228,7 @@ def update_profile(id, profile_partial_update_query, opts = {})
22132228
# @param id [String] Primary key that uniquely identifies this profile. Generated by Klaviyo.
22142229
# @param profile_partial_update_query [ProfilePartialUpdateQuery]
22152230
# @param [Hash] opts the optional parameters
2231+
# @option opts [Array<String>] :additional_fields_profile Request additional fields not included by default in the response. Supported values: &#39;subscriptions&#39;, &#39;predictive_analytics&#39;
22162232
# @return [Array<(Hash<String, Object>, Integer, Hash)>] Hash<String, Object> data, response status code and response headers
22172233
def update_profile_with_http_info(id, profile_partial_update_query, opts = {})
22182234
if @api_client.config.debugging
@@ -2226,11 +2242,16 @@ def update_profile_with_http_info(id, profile_partial_update_query, opts = {})
22262242
if @api_client.config.client_side_validation && profile_partial_update_query.nil?
22272243
fail ArgumentError, "Missing the required parameter 'profile_partial_update_query' when calling ProfilesApi.update_profile"
22282244
end
2245+
allowable_values = ["subscriptions", "predictive_analytics"]
2246+
if @api_client.config.client_side_validation && opts[:'additional_fields_profile'] && !opts[:'additional_fields_profile'].all? { |item| allowable_values.include?(item) }
2247+
fail ArgumentError, "invalid value for \"additional_fields_profile\", must include one of #{allowable_values}"
2248+
end
22292249
# resource path
22302250
local_var_path = '/api/profiles/{id}'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
22312251

22322252
# query parameters
22332253
query_params = opts[:query_params] || {}
2254+
query_params[:'additional-fields[profile]'] = @api_client.build_collection_param(opts[:'additional_fields_profile'], :csv) if !opts[:'additional_fields_profile'].nil?
22342255

22352256
# header parameters
22362257
header_params = opts[:header_params] || {}

lib/klaviyo-api-sdk/api_client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ApiClient
3232
# @option config [Configuration] Configuration for initializing the object, default to Configuration.default
3333
def initialize(config = Configuration.default)
3434
@config = config
35-
@user_agent = "klaviyo-api-ruby/10.0.0"
35+
@user_agent = "klaviyo-api-ruby/11.0.0"
3636
@default_headers = {
3737
'Content-Type' => 'application/json',
3838
'User-Agent' => @user_agent

lib/klaviyo-api-sdk/models/device_metadata.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def list_invalid_properties
206206
# Check to see if the all the properties in the model are valid
207207
# @return true if the model is valid
208208
def valid?
209-
klaviyo_sdk_validator = EnumAttributeValidator.new('String', ["android", "react_native", "swift"])
209+
klaviyo_sdk_validator = EnumAttributeValidator.new('String', ["android", "flutter_community", "react_native", "swift"])
210210
return false unless klaviyo_sdk_validator.valid?(@klaviyo_sdk)
211211
os_name_validator = EnumAttributeValidator.new('String', ["android", "ios", "ipados", "macos", "tvos"])
212212
return false unless os_name_validator.valid?(@os_name)
@@ -218,7 +218,7 @@ def valid?
218218
# Custom attribute writer method checking allowed values (enum).
219219
# @param [Object] klaviyo_sdk Object to be assigned
220220
def klaviyo_sdk=(klaviyo_sdk)
221-
validator = EnumAttributeValidator.new('String', ["android", "react_native", "swift"])
221+
validator = EnumAttributeValidator.new('String', ["android", "flutter_community", "react_native", "swift"])
222222
unless validator.valid?(klaviyo_sdk)
223223
fail ArgumentError, "invalid value for \"klaviyo_sdk\", must be one of #{validator.allowable_values}."
224224
end

lib/klaviyo-api-sdk/models/html_block_data.rb

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,11 @@ class HTMLBlockData
1919

2020
attr_accessor :display_options
2121

22-
attr_accessor :styles
23-
2422
# Attribute mapping from ruby-style variable name to JSON key.
2523
def self.attribute_map
2624
{
2725
:'content' => :'content',
28-
:'display_options' => :'display_options',
29-
:'styles' => :'styles'
26+
:'display_options' => :'display_options'
3027
}
3128
end
3229

@@ -39,15 +36,13 @@ def self.acceptable_attributes
3936
def self.openapi_types
4037
{
4138
:'content' => :'String',
42-
:'display_options' => :'BlockDisplayOptions',
43-
:'styles' => :'String'
39+
:'display_options' => :'BlockDisplayOptions'
4440
}
4541
end
4642

4743
# List of attributes with nullable: true
4844
def self.openapi_nullable
4945
Set.new([
50-
:'styles'
5146
])
5247
end
5348

@@ -73,10 +68,6 @@ def initialize(attributes = {})
7368
if attributes.key?(:'display_options')
7469
self.display_options = attributes[:'display_options']
7570
end
76-
77-
if attributes.key?(:'styles')
78-
self.styles = attributes[:'styles']
79-
end
8071
end
8172

8273
# Show invalid properties with the reasons. Usually used together with valid?
@@ -108,8 +99,7 @@ def ==(o)
10899
return true if self.equal?(o)
109100
self.class == o.class &&
110101
content == o.content &&
111-
display_options == o.display_options &&
112-
styles == o.styles
102+
display_options == o.display_options
113103
end
114104

115105
# @see the `==` method
@@ -121,7 +111,7 @@ def eql?(o)
121111
# Calculates hash code according to all attributes.
122112
# @return [Integer] Hash code
123113
def hash
124-
[content, display_options, styles].hash
114+
[content, display_options].hash
125115
end
126116

127117
# Builds the object from hash

0 commit comments

Comments
 (0)