Skip to content

Commit 6a2da90

Browse files
author
klaviyo-sdk
committed
version 11.0.0
1 parent 8dfd382 commit 6a2da90

File tree

547 files changed

+62139
-2091
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

547 files changed

+62139
-2091
lines changed

.gitkeep

Whitespace-only changes.

CHANGELOG.md

+1-70
Original file line numberDiff line numberDiff line change
@@ -7,76 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
NOTE: For more granular API-specific changes, please see our [API Changelog](https://developers.klaviyo.com/en/docs/changelog_)
99

10-
## [10.0.0] - revision 2024-07-15
1110

12-
### Added
13-
14-
- Forms API
15-
- New `klaviyo.Forms` class with methods to get forms, form versions and relationships
16-
- Webhooks API
17-
- new `klaviyo.Webooks` class containing CRUD operations for webhooks
18-
19-
### Changed
20-
- `klaviyo.Profiles.subscribe()`
21-
- added `historical_import` flag for importing historically consented profiles can now be optionally supplied in the payload for the Subscribe Profiles endpoint.
22-
- When using this flag, a consented_at date must be provided and must be in the past.
23-
24-
## [9.0.0] - revision 2024-06-15
25-
26-
### Added
27-
- Segments Api
28-
- New create segment endpoint `SegmentsApi.createSegment()`.
29-
- New delete segment endpoint `SegementsApi.deleteSegment()`.
30-
- Updated exisiting segments endpoints to include the segment definition
31-
- For more information, see our [Segments API overview](https://developers.klaviyo.com/en/reference/segments_api_overview).
32-
33-
- Flows Api
34-
- New delete flows endpoint `FlowsApi.deleteFlow()`
35-
36-
## [8.0.1] - revision 2024-05-15
37-
38-
### Added
39-
40-
- Fixes issue where `filter` query params for any API call were being duplicated on request send. See issue: https://github.com/klaviyo/klaviyo-api-python/issues/51
41-
42-
## [8.0.0] - revision 2024-05-15
43-
44-
### Added
45-
46-
- Bulk Create Events API with
47-
- We have added support for creating events in bulk via the EventsApi.bulkCreateEvents method
48-
- Create multiple events for new and existing profiles and/or update profile properties in a single API call. For more information, see our [Events API overview](https://developers.klaviyo.com/en/reference/events_api_overview).
49-
50-
### Changed
51-
52-
- Accounts API
53-
- `Accounts.get_account` and `Accounts.get_accounts` have been updated to return the account's locale, e.g. `"en-US"`.
54-
55-
- **Breaking**
56-
- Subscribe API Synchronous Validation Improved
57-
- To provide better feedback for handling SMS subscriptions, we’ve added improved validation behavior to ProfilesApi.subscribeProfiles method. In prior revisions, such requests may appear as 202s but will fail to update SMS consent. To handle this issue, 400 validation errors are returned for the following cases
58-
1. If a profile is subscribed to SMS marketing and [age-gating is enabled](https://help.klaviyo.com/hc/en-us/articles/4408311712667) but age_gated_date_of_birth is not provided, or the DOB does not meet the region's requirements.
59-
2. If the account does not have a sending number in the phone number’s region.
60-
3. If the phone number is in a region not supported by Klaviyo.
61-
4. If consented_at is set and the list or global setting is double opt-in.
62-
- Pydantic V2
63-
- This SDK now uses Pydantic V2. This may cause some compatibility issues if your source code depends on Pydantic V1.
64-
- Renamed Fields in SDK
65-
- As of the 2024-05-15 release, some models fields are named differently than they appear in API documentation. These fields are
66-
- `datetime`: renamed to `datetime_`
67-
- `date`: renamed to `date_`
68-
69-
This is to manage compatibility with Pydantic v2. An example of this can be seen in [StaticScheduleOptions](src/openapi_client/models/static_schedule_options.py).
70-
71-
```python
72-
class StaticScheduleOptions(BaseModel):
73-
"""
74-
StaticScheduleOptions
75-
""" # noqa: E501
76-
datetime_: datetime = Field(description="The time to send at", alias="datetime")
77-
78-
schedule_options = StaticScheduleOptions(datetime_=datetime.datetime.strptime("2024-05-19T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
79-
print(schedule_options.datetime_)
8011

8112
## [7.0.0] - revision 2024-02-15
8213

@@ -351,4 +282,4 @@ For EmailMarketing:
351282
- client name: `Client``KlaviyoAPI`
352283
- Client variable name in readme examples: `client``klaviyo`
353284
- Some functions have changed name
354-
- New resources and endpoints: see [API Changelog](https://developers.klaviyo.com/en/docs/changelog_) for full details
285+
- New resources and endpoints: see [API Changelog](https://developers.klaviyo.com/en/docs/changelog_) for full details

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Klaviyo Python SDK
22

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

66
## Table of Contents

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "openapi_client"
3-
version = "10.0.0"
3+
version = "11.0.0"
44
description = "Klaviyo API"
55
authors = ["Klaviyo Developer Experience Team <[email protected]>"]
66
license = "License"

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = klaviyo-api
3-
version = 10.0.0
3+
version = 11.0.0
44
author = Klaviyo Developers
55
author_email = [email protected]
66
description = Klaviyo Python SDK

src/klaviyo_api/filter_builder.py

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import datetime
2+
import json
3+
import re
4+
5+
from typing import List, Union
6+
7+
8+
class FilterBuilder:
9+
def __init__(self):
10+
self.filters = []
11+
12+
def _add_value_filter(self, allowed_types, operator, field, value, allow_list):
13+
self._verify_field_is_string(field)
14+
15+
type_check_value = value
16+
if isinstance(value, list) and allow_list:
17+
self._verify_list_of_single_type(value, allowed_types)
18+
type_check_value = value[0]
19+
20+
if not isinstance(type_check_value, allowed_types):
21+
raise TypeError(f'value must be a {allowed_types}')
22+
23+
if isinstance(value, list):
24+
value = [v.isoformat() if isinstance(v, datetime.datetime) else v for v in value]
25+
elif isinstance(value, datetime.datetime):
26+
value = value.isoformat()
27+
else:
28+
value = self._toJson(value)
29+
30+
# python surrounds datetime values in a list with single quotes
31+
if isinstance(type_check_value, datetime.datetime):
32+
value = re.sub("'", '', f'{value}')
33+
34+
constructed_filter = re.sub(r'\s', '', f'{operator}({field},{value})')
35+
self.filters.append(constructed_filter)
36+
37+
def equals(self, field: str, value: Union[list, bool, int, float, str, datetime.datetime]):
38+
types = (bool, int, float, str, datetime.datetime)
39+
self._add_value_filter(types, 'equals', field, value, allow_list=True)
40+
41+
def less_than(self, field: str, value: Union[int, float, datetime.datetime]):
42+
types = (int, float, datetime.datetime)
43+
self._add_value_filter(types, 'less-than', field, value, allow_list=False)
44+
45+
def less_or_equal(self, field: str, value: Union[int, float, datetime.datetime]):
46+
types = (int, float, datetime.datetime)
47+
self._add_value_filter(types, 'less-or-equal', field, value, allow_list=False)
48+
49+
def greater_than(self, field: str, value: Union[int, float, datetime.datetime]):
50+
types = (int, float, datetime.datetime)
51+
self._add_value_filter(types, 'greater-than', field, value, allow_list=False)
52+
53+
def greater_or_equal(self, field: str, value: Union[int, float, datetime.datetime]):
54+
types = (int, float, datetime.datetime)
55+
self._add_value_filter(types, 'greater-or-equal', field, value, allow_list=False)
56+
57+
def contains(self, field: str, value: Union[str, list]):
58+
types = (str,)
59+
self._add_value_filter(types, 'contains', field, value, allow_list=True)
60+
61+
def contains_any(self, field: str, value: Union[str, list]):
62+
types = (str,)
63+
self._add_value_filter(types, 'contains-any', field, value, allow_list=True)
64+
65+
def contains_all(self, field: str, value: Union[str, list]):
66+
types = (str,)
67+
self._add_value_filter(types, 'contains-all', field, value, allow_list=True)
68+
69+
def ends_with(self, field: str, value: str):
70+
types = (str,)
71+
self._add_value_filter(types, 'ends-with', field, value, allow_list=False)
72+
73+
def starts_with(self, field: str, value: str):
74+
types = (str,)
75+
self._add_value_filter(types, 'starts-with', field, value, allow_list=False)
76+
77+
def any(self, field: str, value: List[Union[str, bool, int, float, datetime.datetime]]):
78+
if type(value) is not list:
79+
raise TypeError('value must be a list')
80+
types =(bool, int, float, str, datetime.datetime)
81+
self._add_value_filter(types, 'any', field, value, allow_list=True)
82+
83+
def has(self, field: str):
84+
self._verify_field_is_string(field)
85+
self.filters.append(f'has({field})')
86+
87+
def _verify_field_is_string(self, field):
88+
if not isinstance(field, str):
89+
raise TypeError('field must be a string')
90+
91+
def _verify_list_of_single_type(self, value, allowed_types):
92+
if not value: # if list is empty, we cannot determine the type
93+
raise ValueError('value cannot be an empty list')
94+
95+
first_type = type(value[0])
96+
97+
if first_type not in allowed_types:
98+
raise TypeError(f'Items in list must be one of the following types: {allowed_types}')
99+
100+
if not all(isinstance(v, first_type) for v in value):
101+
raise TypeError('All items in value must be of the same type')
102+
103+
def _toJson(self, value):
104+
return json.dumps(value, separators=(',', ':'))
105+
106+
def build(self):
107+
return ','.join(self.filters)

src/klaviyo_api/wrapper.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import openapi_client
88
import klaviyo_api.custom_retry as custom_retry
9-
from dataclasses import dataclass
10-
from typing import Callable, ClassVar
9+
from dataclasses import dataclass, field
10+
from typing import Callable, ClassVar, Dict, Any
1111
from openapi_client.api import accounts_api
1212
from openapi_client.api import campaigns_api
1313
from openapi_client.api import catalogs_api
@@ -35,6 +35,7 @@ class KlaviyoAPI:
3535
max_retries: int = 3
3636
test_host: str = ''
3737
access_token: str = None
38+
options: Dict[str, Any] = field(default_factory=dict)
3839

3940

4041
_REVISION = "2024-07-15"
@@ -73,7 +74,7 @@ def __post_init__(self):
7374
if self.test_host:
7475
self.configuration.host = self.test_host
7576

76-
self.api_client = openapi_client.ApiClient(self.configuration)
77+
self.api_client = openapi_client.ApiClient(self.configuration, options=self.options)
7778

7879
self.api_client.default_headers['revision'] = self._REVISION
7980

0 commit comments

Comments
 (0)