Skip to content
This repository has been archived by the owner on Aug 18, 2023. It is now read-only.

Commit

Permalink
Merge pull request #9 from klaviyo/client-refactor
Browse files Browse the repository at this point in the history
Refactored Client after thorough code review
  • Loading branch information
jon-batscha committed Jul 20, 2022
2 parents bbc09d8 + 5e9bca5 commit 43eec03
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Klaviyo Python SDK

- SDK version: 1.0.5.20220329
- SDK version: 1.0.6.20220329

## Helpful Resources

Expand Down
102 changes: 65 additions & 37 deletions klaviyo_sdk/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,51 @@
import json
import base64
import requests
from dataclasses import dataclass
from typing import Callable, ClassVar


@dataclass
class Client:

def __init__(self, api_key,test_host=None, max_delay=60, max_retries=3):
api_key: str
max_delay: int = 60
max_retries: int = 3
test_host: str = None

_STATUS_CODE_TOO_MANY_REQUESTS = 429
_STATUS_CODE_SERVICE_UNAVAILABLE = 503
_STATUS_CODE_GATEWAY_TIMEOUT = 504
_STATUS_CODE_A_TIMEOUT_OCCURED = 524

_RETRY_CODES = {
_STATUS_CODE_TOO_MANY_REQUESTS,
_STATUS_CODE_SERVICE_UNAVAILABLE,
_STATUS_CODE_GATEWAY_TIMEOUT,
_STATUS_CODE_A_TIMEOUT_OCCURED
}


def __post_init__(self):

configuration = swagger_client.Configuration()

self.api_key = api_key
configuration.api_key["api_key"] = self.api_key
if self.test_host:
configuration.host = self.test_host

self.retry_codes = [429,503,504,524]
## Defining retry logic
if self.max_retries <= 0:
self.max_wait = .1
else:
self.max_wait = self.max_delay/self.max_retries

self.retry_logic = tenacity.retry(
reraise=True,
retry=custom_retry.retry_if_qualifies(self.retry_codes),
wait=tenacity.wait.wait_random_exponential(multiplier = 1, max = max_delay/max_retries),
stop=tenacity.stop.stop_after_attempt(max_retries)
retry=custom_retry.retry_if_qualifies(self._RETRY_CODES),
wait=tenacity.wait.wait_random_exponential(multiplier = 1, max = self.max_wait),
stop=tenacity.stop.stop_after_attempt(self.max_retries)
)

configuration.api_key["api_key"] = api_key
if test_host:
configuration.host = test_host

## Adding Campaigns to Client
self.Campaigns=swagger_client.CampaignsApi(swagger_client.ApiClient(configuration))
Expand Down Expand Up @@ -115,19 +138,23 @@ def __init__(self, api_key,test_host=None, max_delay=60, max_retries=3):
self.TrackIdentify.track_post=self.retry_logic(self.TrackIdentify.track_post)


self.TrackIdentify.track_post = self.post_update(self.TrackIdentify.track_post)
self.TrackIdentify.identify_post = self.post_update(self.TrackIdentify.identify_post)
self.TrackIdentify.track_get = self.get_update(self.TrackIdentify.track_get)
self.TrackIdentify.identify_get = self.get_update(self.TrackIdentify.identify_get)

self.Profiles.update_profile = self.update_profile_fix(self.Profiles.update_profile)
## Applying custom fix decorators where needed
self.TrackIdentify.track_post = self._post_update(self.TrackIdentify.track_post)
self.TrackIdentify.identify_post = self._post_update(self.TrackIdentify.identify_post)
self.TrackIdentify.track_get = self._get_update(self.TrackIdentify.track_get)
self.TrackIdentify.identify_get = self._get_update(self.TrackIdentify.identify_get)
self.Profiles.update_profile = self._update_profile_fix(self.Profiles.update_profile)

def is_error(self, status):
@staticmethod
def _is_error(status: int) -> bool:

return not (200 <= status <= 299)

def update_profile_fix(self, func):
def wrapped_func(person_id='PERSON_ID', params={}):
def _update_profile_fix(self, func: Callable) -> Callable:
def _wrapped_func(person_id: str=None, params: dict=None) -> dict:

if params is None:
params = {}

url = f"https://a.klaviyo.com/api/v1/person/{person_id}"

Expand All @@ -139,53 +166,54 @@ def wrapped_func(person_id='PERSON_ID', params={}):

headers = {
"Accept": "application/json",
"user-agent" : "klaviyo-python-sdk/1.0.5.20220329"
"user-agent" : "klaviyo-python-sdk/1.0.6.20220329"
}

response = requests.request("PUT", url, headers=headers, params=querystring)
response = requests.request("PUT", url, headers=headers, params=querystring, timeout=self.max_wait)

if self.is_error(response.status_code):
if self._is_error(response.status_code):

e = swagger_client.rest.ApiException(status=response.status_code, reason=response.reason, http_resp=response)
raise(e)

return response.json()

return wrapped_func

return _wrapped_func


def post_update(self, func):
def wrapped_func(data={}):
if type(data) is not str:
@classmethod
def _post_update(cls, func: Callable) -> Callable:
def _wrapped_func(data: dict={}):
if not isinstance(data, str):
data = json.dumps(data)
return func(data=data)
return wrapped_func

return _wrapped_func

def get_update(self, func):
def wrapped_func(data={}):
@classmethod
def _get_update(cls, func: Callable) -> Callable:
def _wrapped_func(data={}):

if type(data) is dict:
if isinstance(data, dict):
json_string = json.dumps(data)
utf = json_string.encode('utf-8')
data = base64.b64encode(utf)

return func(data)

elif type(data) is str:
elif isinstance(data, str):
utf = data.encode('utf-8')
data = base64.b64encode(utf)

return func(data)

elif type(data) is bytes:
elif isinstance(data, bytes):

if b'{' in data:
data = base64.b64encode(data)

return func(data)

return wrapped_func


else:
raise TypeError("data must be a dict, str, or bytes")

return _wrapped_func
2 changes: 1 addition & 1 deletion swagger_client/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __init__(self, configuration=None, header_name=None, header_value=None,
self.default_headers[header_name] = header_value
self.cookie = cookie
# Set default User-Agent.
self.user_agent = 'klaviyo-python-sdk/1.0.5.20220329'
self.user_agent = 'klaviyo-python-sdk/1.0.6.20220329'

def __del__(self):
if self._pool is not None:
Expand Down

0 comments on commit 43eec03

Please sign in to comment.