Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Prices.get shall be our first victim
Browse files Browse the repository at this point in the history
  • Loading branch information
Invincibear committed Jan 25, 2024
1 parent e388876 commit f53430e
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 36 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

setup(
name='paddle-billing-python-sdk',
version='0.0.1a20',
version='0.0.1a21',
author='Corey Regan',
author_email='[email protected]',
description='A Python wrapper for the Paddle Billing API',
Expand Down
111 changes: 76 additions & 35 deletions src/Client.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,84 @@
import requests
from logging import getLogger
from json import dumps as json_dumps
from requests import request, RequestException, Session
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from urllib.parse import urljoin, urlencode
from urllib.parse import urljoin, urlencode
from uuid import uuid4

from json import dumps as json_dumps
from urllib.parse import urlparse

from src.HasParameters import HasParameters
from src.Options import Options

# from src.Logger.Formatter import CustomLogger
# from src.Entities.Addresses.AddressesClient import AddressesClient
# from src.Entities.Adjustment.AdjustmentsClient import AdjustmentsClient
# from src.Entities.Businesses.BusinessesClient import BusinessesClient
# from src.Entities.Customers.CustomersClient import CustomersClient
# from src.Entities.Discounts.DiscountsClient import DiscountsClient
# from src.Entities.Events.EventsClient import EventsClient
# from src.Entities.EventTypes.EventTypesClient import EventTypesClient
# from src.Entities.NotificationLogs.NotificationLogsClient import NotificationLogsClient
# from src.Entities.Notifications.NotificationsClient import NotificationsClient
# from src.Entities.NotificationSettings.NotificationSettingsClient import NotificationSettingsClient
# from src.Entities.Prices.PricesClient import PricesClient
# from src.Entities.PricingPreviews.PricingPreviewsClient import PricingPreviewsClient
# from src.Entities.Products.ProductsClient import ProductsClient
# from src.Entities.Reports.ReportsClient import ReportsClient
# from src.Entities.Subscriptions.SubscriptionsClient import SubscriptionsClient
# from src.Entities.Transactions.TransactionsClient import TransactionsClient
from src.Logger.NullHandler import NullHandler

# from src.Resources.Addresses.AddressesClient import AddressesClient
# from src.Resources.Adjustment.AdjustmentsClient import AdjustmentsClient
# from src.Resources.Businesses.BusinessesClient import BusinessesClient
# from src.Resources.Customers.CustomersClient import CustomersClient
# from src.Resources.Discounts.DiscountsClient import DiscountsClient
# from src.Resources.Events.EventsClient import EventsClient
# from src.Resources.EventTypes.EventTypesClient import EventTypesClient
# from src.Resources.NotificationLogs.NotificationLogsClient import NotificationLogsClient
# from src.Resources.Notifications.NotificationsClient import NotificationsClient
# from src.Resources.NotificationSettings.NotificationSettingsClient import NotificationSettingsClient
from src.Resources.Prices.PricesClient import PricesClient
# from src.Resources.PricingPreviews.PricingPreviewsClient import PricingPreviewsClient
# from src.Resources.Products.ProductsClient import ProductsClient
# from src.Resources.Reports.ReportsClient import ReportsClient
# from src.Resources.Subscriptions.SubscriptionsClient import SubscriptionsClient
# from src.Resources.Transactions.TransactionsClient import TransactionsClient


class Client:
"""
Client for making API requests using Python's requests library.
"""

SDK_VERSION = '0.0.1a20'
SDK_VERSION = '0.0.1a21'


def __init__(
self,
api_key: str, # handle our api key class
options: dict = None,
http_client: requests = None,
http_client: Session = None,
logger = None,
retry_count: int = 3,
):
self.api_key = api_key
self.options = options if options else {}
self.logger = logger
self.__api_key = api_key
self.options = options if options else Options()
self.logger = logger if logger else Client.null_logger()
self.retry_count = retry_count
self.transaction_id = None
self.client = self.build_request_session()
self.client = self.build_request_session() if not http_client else http_client

# TODO
# Initialize other clients as needed
# self.products = ProductsClient(self)
self.prices = PricesClient(self)
# ... Initialize other resource clients here ...


@staticmethod
def null_logger():
"""
Create a logger instance that logs everything to nowhere
"""

null_logger = getLogger('null_logger')
null_logger.addHandler(NullHandler())

return null_logger


def logging_hook(self, response, *args, **kwargs):
self.logger.info(f"Request: {response.request.method} {response.request.url}")
self.logger.info(f"Response: {response.status_code} {response.text}")
"""
Requests logs were redirected here and to our custom logger which filters out sensitive data
"""

self.logger.debug(f"Request: {response.request.method} {response.request.url}")
self.logger.debug(f"Response: {response.status_code} {response.text}")


def _make_request(
Expand All @@ -66,6 +87,10 @@ def _make_request(
uri: str,
payload: dict | None = None,
):
"""
Makes an actual API call to Paddle
"""

# Parse and update URI with base URL components if necessary
if isinstance(uri, str):
uri = urljoin(self.options['environment'].base_url, uri)
Expand All @@ -76,7 +101,7 @@ def _make_request(

# Serialize payload to JSON
final_json = None
if payload is not None:
if payload:
json_payload = json_dumps(payload)
final_json = json_payload if json_payload == '[]' else '{}'

Expand All @@ -86,18 +111,34 @@ def _make_request(
response.raise_for_status()

return response
except requests.RequestException as e:
except RequestException as e:
if self.logger:
self.logger.error(f"Request failed: {e}")
raise


@staticmethod
def _format_uri_parameters(uri, parameters: HasParameters):
if isinstance(parameters, HasParameters):
parameters = parameters.get_parameters()

query = urlencode(parameters)
uri += '&' if '?' in uri else '?'
uri += query

return uri


def get_raw(self, uri, parameters=None):
return self._make_request('GET', uri, None, parameters)
uri = Client._format_uri_parameters(uri, parameters) if parameters else uri

return self._make_request('GET', uri, None)


def post_raw(self, uri, payload=None, parameters=None):
return self._make_request('POST', uri, payload, parameters)
uri = Client._format_uri_parameters(uri, parameters) if parameters else uri

return self._make_request('POST', uri, payload)


def patch_raw(self, uri, payload):
Expand All @@ -109,9 +150,9 @@ def delete_raw(self, uri):


def build_request_session(self):
session = requests.Session()
session = Session()
session.headers.update({
'Authorization': f"Bearer {self.api_key}",
'Authorization': f"Bearer {self.__api_key}",
'Content-Type': 'application/json',
'User-Agent': f"paddle-billing-python-sdk {self.SDK_VERSION}",
})
Expand Down
6 changes: 6 additions & 0 deletions src/Logger/NullHandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import logging


class NullHandler(logging.Handler):
def emit(self, record):
pass
5 changes: 5 additions & 0 deletions src/Resources/Prices/Operations/List/Includes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from enum import StrEnum


class Includes(StrEnum):
Product = 'product'
51 changes: 51 additions & 0 deletions src/Resources/Prices/Operations/ListPrices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from src.Entities.Shared.CatalogType import CatalogType
from src.Entities.Shared.Status import Status

from src.Exceptions.SdkExceptions.InvalidArgumentException import InvalidArgumentException

from src.Resources.Prices.Operations.List.Includes import Includes


class ListPrices:
def __init__(self, pager=None, includes=None, ids=None, types=None, product_ids=None, statuses=None, recurring=None):
self.pager = pager
self.includes = includes if includes is not None else []
self.ids = ids if ids is not None else []
self.types = types if types is not None else []
self.product_ids = product_ids if product_ids is not None else []
self.statuses = statuses if statuses is not None else []
self.recurring = recurring

# Validation
if any(not isinstance(include, Includes) for include in self.includes):
raise InvalidArgumentException('includes', Includes.__name__)
if any(not isinstance(i, str) for i in self.ids):
raise InvalidArgumentException('ids', 'string')
if any(not isinstance(t, CatalogType) for t in self.types):
raise InvalidArgumentException('types', CatalogType.__name__)
if any(not isinstance(pid, str) for pid in self.product_ids):
raise InvalidArgumentException('productIds', 'string')
if any(not isinstance(status, Status) for status in self.statuses):
raise InvalidArgumentException('statuses', Status.__name__)


def get_parameters(self):
enum_stringify = lambda enum: enum.value # noqa E731

parameters = {}
if self.pager:
parameters.update(self.pager.get_parameters())
if self.includes:
parameters['include'] = ','.join(map(enum_stringify, self.includes))
if self.ids:
parameters['id'] = ','.join(self.ids)
if self.types:
parameters['type'] = ','.join(map(enum_stringify, self.types))
if self.product_ids:
parameters['product_id'] = ','.join(self.product_ids)
if self.statuses:
parameters['status'] = ','.join(map(enum_stringify, self.statuses))
if self.recurring is not None:
parameters['recurring'] = 'true' if self.recurring else 'false'

return parameters
58 changes: 58 additions & 0 deletions src/Resources/Prices/PricesClient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from src.ResponseParser import ResponseParser

from src.Entities.PriceWithIncludes import PriceWithIncludes

from src.Entities.Collections.Paginator import Paginator
from src.Entities.Collections.PriceWithIncludesCollection import PriceWithIncludesCollection

from src.Entities.Shared.Status import Status

# from src.Resources.Prices.Operations.CreatePrice import CreatePrice
from src.Resources.Prices.Operations.ListPrices import ListPrices
# from src.Resources.Prices.Operations.UpdatePrice import UpdatePrice


class PricesClient:
def __init__(self, client):
self.client = client


def list(self, list_operation: ListPrices = None):
if list_operation is None:
list_operation = ListPrices()

response = self.client.get_raw('/prices', list_operation.get_parameters())
parser = ResponseParser(response)
return PriceWithIncludesCollection.from_list(parser.get_data(), Paginator(self.client, parser.get_pagination(), PriceWithIncludesCollection))


def get(self, price_id: str, includes = None):
if includes is None:
includes = []

# Validate 'includes' items and build parameters
params = {'include': ','.join(include.value for include in includes)} if includes else {}
response = self.client.get_raw(f"/prices/{price_id}", params)
parser = ResponseParser(response)

return PriceWithIncludes.from_dict(parser.get_data())


# def create(self, create_operation: CreatePrice):
# response = self.client.post_raw('/prices', create_operation.get_parameters())
# parser = ResponseParser(response)
#
# return PriceWithIncludes.from_dict(parser.get_data())
#
#
# def update(self, price_id: str, operation: UpdatePrice):
# response = self.client.patch_raw(f"/prices/{price_id}", operation.get_parameters())
# parser = ResponseParser(response)
#
# return PriceWithIncludes.from_dict(parser.get_data())
#
#
# def archive(self, price_id: str):
# operation = UpdatePrice(status=Status.Archived)
#
# return self.update(price_id, operation)

0 comments on commit f53430e

Please sign in to comment.