diff --git a/README.rst b/README.rst index 8d8780d..fa638e4 100644 --- a/README.rst +++ b/README.rst @@ -20,8 +20,10 @@ Connecting # Public apps (OAuth) # Access_token is optional, if you don't have one you can use oauth_fetch_token (see below) + For connecting to the v2 api: api = bigcommerce.api.BigcommerceApi(client_id='', store_hash='', access_token='') - + For connecting to the v3 api: + api = bigcommerce.api.BigcommerceApi(client_id='', store_hash='', access_token='', api_path='/stores/{}/v3/{}')) # Private apps (Basic Auth) api = bigcommerce.api.BigcommerceApi(host='store.mybigcommerce.com', basic_auth=('username', 'api token')) diff --git a/bigcommerce/api.py b/bigcommerce/api.py index 057a4d3..404a71c 100644 --- a/bigcommerce/api.py +++ b/bigcommerce/api.py @@ -5,7 +5,7 @@ class BigcommerceApi(object): - def __init__(self, host=None, basic_auth=None, + def __init__(self, host=None, api_path=None, basic_auth=None, client_id=None, store_hash=None, access_token=None, rate_limiting_management=None): self.api_service = os.getenv('BC_API_ENDPOINT', 'api.bigcommerce.com') self.auth_service = os.getenv('BC_AUTH_SERVICE', 'login.bigcommerce.com') @@ -14,6 +14,7 @@ def __init__(self, host=None, basic_auth=None, self.connection = connection.Connection(host, basic_auth) elif client_id and store_hash: self.connection = connection.OAuthConnection(client_id, store_hash, access_token, self.api_service, + api_path=api_path, rate_limiting_management=rate_limiting_management) else: raise Exception("Must provide either (client_id and store_hash) or (host and basic_auth)") diff --git a/bigcommerce/connection.py b/bigcommerce/connection.py index 7bffb89..8c6df1d 100644 --- a/bigcommerce/connection.py +++ b/bigcommerce/connection.py @@ -53,6 +53,13 @@ def _run_method(self, method, url, data=None, query=None, headers=None): if headers is None: headers = {} + # Support v3 + if self.api_path and 'v3' in self.api_path: + if url is 'orders': + self.api_path = self.api_path.replace('v3', 'v2') + else: + url = 'catalog/{}'.format(url) + # make full path if not given if url and url[:4] != "http": if url[0] == '/': # can call with /resource if you want @@ -156,6 +163,9 @@ def _handle_response(self, url, res, suppress_empty=True): if res.status_code in (200, 201, 202): try: result = res.json() + # Support v3 + if self.api_path and 'v3' in self.api_path: + result = result['data'] # TODO ignore meta field for now except Exception as e: # json might be invalid, or store might be down e.message += " (_handle_response failed to decode JSON: " + str(res.content) + ")" raise # TODO better exception @@ -187,11 +197,12 @@ class OAuthConnection(Connection): """ def __init__(self, client_id, store_hash, access_token=None, host='api.bigcommerce.com', - api_path='/stores/{}/v2/{}', rate_limiting_management=None): + api_path=None, rate_limiting_management=None): self.client_id = client_id self.store_hash = store_hash self.host = host self.api_path = api_path + self.api_path = api_path if api_path else "/stores/{}/v2/{}" self.timeout = 7.0 # can attach to session? self.rate_limiting_management = rate_limiting_management diff --git a/bigcommerce/resources/__init__.py b/bigcommerce/resources/__init__.py index 541fc4f..c5b0f0d 100644 --- a/bigcommerce/resources/__init__.py +++ b/bigcommerce/resources/__init__.py @@ -20,4 +20,5 @@ from .store import * from .tax_classes import * from .time import * +from .variants import * from .webhooks import * diff --git a/bigcommerce/resources/products.py b/bigcommerce/resources/products.py index d569de9..a45efa7 100644 --- a/bigcommerce/resources/products.py +++ b/bigcommerce/resources/products.py @@ -48,11 +48,17 @@ def rules(self, id=None): else: return ProductRules.all(self.id, connection=self._connection) - def skus(self, id=None): + def skus(self, id=None, **kwargs): if id: - return ProductSkus.get(self.id, id, connection=self._connection) + return ProductSkus.get(self.id, id, connection=self._connection, **kwargs) else: - return ProductSkus.all(self.id, connection=self._connection) + return ProductSkus.all(self.id, connection=self._connection, **kwargs) + + def variants(self, id=None, **kwargs): + if id: + return ProductVariants.get(self.id, id, connection=self._connection, **kwargs) + else: + return ProductVariants.all(self.id, connection=self._connection, **kwargs) def videos(self, id=None): if id: @@ -99,7 +105,9 @@ class ProductImages(ListableApiSubResource, CreateableApiSubResource, count_resource = 'products/images' -class ProductOptions(ListableApiSubResource): +class ProductOptions(ListableApiSubResource, CreateableApiSubResource, + UpdateableApiSubResource, DeleteableApiSubResource, + CollectionDeleteableApiSubResource, CountableApiSubResource): resource_name = 'options' parent_resource = 'products' parent_key = 'product_id' @@ -131,6 +139,13 @@ class ProductSkus(ListableApiSubResource, CreateableApiSubResource, parent_key = 'product_id' count_resource = 'products/skus' +class ProductVariants(ListableApiSubResource, CreateableApiSubResource, + UpdateableApiSubResource, DeleteableApiSubResource, + CollectionDeleteableApiSubResource, CountableApiSubResource): + resource_name = 'variants' + parent_resource = 'products' + parent_key = 'product_id' + count_resource = 'products/variants' class ProductVideos(ListableApiSubResource, CountableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource, diff --git a/bigcommerce/resources/variants.py b/bigcommerce/resources/variants.py new file mode 100644 index 0000000..de97ba4 --- /dev/null +++ b/bigcommerce/resources/variants.py @@ -0,0 +1,10 @@ +from .base import * + + +class Variants(ListableApiResource, CreateableApiSubResource, + UpdateableApiSubResource, DeleteableApiSubResource, + CollectionDeleteableApiSubResource, CountableApiSubResource): + resource_name = 'variants' + parent_resource = 'products' + parent_key = 'product_id' + count_resource = 'products/variants' \ No newline at end of file