diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..fa838bc --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,29 @@ +name: Build + +on: + push: + branches: + - bigcli + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + # If you wish to fail your job when the Quality Gate is red, uncomment the + # following lines. This would typically be used to fail a deployment. + # We do not recommend to use this in a pull request. Prefer using pull request + # decoration instead. + # - uses: sonarsource/sonarqube-quality-gate-action@master + # timeout-minutes: 5 + # env: + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index db1cc1e..9d957be 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ Pipfile Pipfile.lock *.egg-info* -venv/ \ No newline at end of file +venv/ +build \ No newline at end of file diff --git a/README.rst b/README.rst index 2c9daca..84442e8 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Bigcommerce API Python Client |Build Status| |Package Version| -Wrapper over the ``requests`` library for communicating with the Bigcommerce v2 API. +Experimental fork of `bigcommerce/bigcommerce-api-python `__ that adds high-level modeling for V3 API objects. Install with ``pip install bigcommerce`` or ``easy_install bigcommerce``. Tested with python 3.8, and only requires ``requests`` and ``pyjwt``. diff --git a/bigcommerce/api.py b/bigcommerce/api.py index 611aaf9..5323463 100644 --- a/bigcommerce/api.py +++ b/bigcommerce/api.py @@ -1,19 +1,20 @@ import os import sys from bigcommerce import connection -from bigcommerce.resources import * # Needed for ApiResourceWrapper dynamic loading - +from bigcommerce.resources.v2 import * # Needed for ApiResourceWrapper dynamic loading +from bigcommerce.resources import v3 # Needed for ApiResourceWrapper dynamic loading class BigcommerceApi(object): def __init__(self, host=None, basic_auth=None, - client_id=None, store_hash=None, access_token=None, rate_limiting_management=None): + client_id=None, store_hash=None, access_token=None, rate_limiting_management=None, version='v2'): self.api_service = os.getenv('BC_API_ENDPOINT', 'api.bigcommerce.com') self.auth_service = os.getenv('BC_AUTH_SERVICE', 'login.bigcommerce.com') + self.version = version if host and basic_auth: 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, + elif (client_id or access_token) and store_hash: + self.connection = connection.OAuthConnection(client_id=client_id, store_hash=store_hash, access_token=access_token, host=self.api_service, rate_limiting_management=rate_limiting_management) else: raise Exception("Must provide either (client_id and store_hash) or (host and basic_auth)") @@ -32,9 +33,12 @@ def oauth_verify_payload_jwt(cls, signed_payload, client_secret, client_id): return connection.OAuthConnection.verify_payload_jwt(signed_payload, client_secret, client_id) def __getattr__(self, item): + if self.version == 'v3': + return V3ApiResourceWrapper(item, self) + if self.version == 'latest': + return TryLatestApiResourceWrapper(item, self) return ApiResourceWrapper(item, self) - class ApiResourceWrapper(object): """ Provides dot access to each of the API resources @@ -71,3 +75,30 @@ def str_to_class(cls, str): Assumes that the class is already loaded. """ return getattr(sys.modules[__name__], str) + +class BigCommerceLatestApi(BigcommerceApi): + def __getattr__(self, item): + return TryLatestApiResourceWrapper(item, self) + + +class V3ApiResourceWrapper(ApiResourceWrapper): + @classmethod + def str_to_class(cls, str): + """ + Transforms a string class name into a class object + Assumes that the class is already loaded. + """ + return getattr(getattr(sys.modules[__name__], 'v3'), str) + + +class TryLatestApiResourceWrapper(ApiResourceWrapper): + @classmethod + def str_to_class(cls, str): + """ + Transforms a string class name into a class object + Assumes that the class is already loaded. + """ + try: + return getattr(getattr(sys.modules[__name__], 'v3'), str) + except AttributeError: + return getattr(sys.modules[__name__], str) diff --git a/bigcommerce/connection.py b/bigcommerce/connection.py index 3012a3e..cbc418f 100644 --- a/bigcommerce/connection.py +++ b/bigcommerce/connection.py @@ -30,10 +30,9 @@ class Connection(object): Connection class manages the connection to the Bigcommerce REST API. """ - def __init__(self, host, auth, api_path='/api/v2/{}'): + def __init__(self, host, auth, api_path='/api/{}/{}'): self.host = host self.api_path = api_path - self.timeout = 7.0 # need to catch timeout? log.info("API Host: %s/%s" % (self.host, self.api_path)) @@ -45,10 +44,14 @@ def __init__(self, host, auth, api_path='/api/v2/{}'): self._last_response = None # for debugging - def full_path(self, url): - return "https://" + self.host + self.api_path.format(url) + def full_path(self, url, version='v2'): + if '/api/{}/{}' in self.api_path: + return "https://" + self.host + self.api_path.format(version, url) + else: + return "https://" + self.host + self.api_path.format(url) + - def _run_method(self, method, url, data=None, query=None, headers=None): + def _run_method(self, method, url, data=None, query=None, headers=None, version='v2'): if query is None: query = {} if headers is None: @@ -58,9 +61,9 @@ def _run_method(self, method, url, data=None, query=None, headers=None): if url and url[:4] != "http": if url[0] == '/': # can call with /resource if you want url = url[1:] - url = self.full_path(url) + url = self.full_path(url, version) elif not url: # blank path - url = self.full_path(url) + url = self.full_path(url, version) qs = urlencode(query) if qs: @@ -127,8 +130,8 @@ def delete(self, resource, rid=None): # note that rid can't be 0 - problem? # Raw-er stuff - def make_request(self, method, url, data=None, params=None, headers=None): - response = self._run_method(method, url, data, params, headers) + def make_request(self, method, url, data=None, params=None, headers=None, version='v2'): + response = self._run_method(method, url, data, params, headers, version=version) return self._handle_response(url, response) def put(self, url, data): @@ -170,6 +173,9 @@ def _handle_response(self, url, res, suppress_empty=True): raise ClientRequestException("%d %s @ %s: %s" % (res.status_code, res.reason, url, res.content), res) elif res.status_code >= 300: raise RedirectionException("%d %s @ %s: %s" % (res.status_code, res.reason, url, res.content), res) + + if 'data' in result: # for v3 + result = result['data'] return result def __repr__(self): @@ -187,8 +193,8 @@ class OAuthConnection(Connection): The verify_payload method is also provided for authenticating signed payloads passed to an application's load url. """ - def __init__(self, client_id, store_hash, access_token=None, host='api.bigcommerce.com', - api_path='/stores/{}/v2/{}', rate_limiting_management=None): + def __init__(self, client_id=None, store_hash=None, access_token=None, host='api.bigcommerce.com', + api_path='/stores/{}/{}/{}', rate_limiting_management=None): self.client_id = client_id self.store_hash = store_hash self.host = host @@ -206,8 +212,10 @@ def __init__(self, client_id, store_hash, access_token=None, host='api.bigcommer self.rate_limit = {} - def full_path(self, url): - return "https://" + self.host + self.api_path.format(self.store_hash, url) + def full_path(self, url, version='v2'): + if '/api/{}/{}/{}' in self.api_path: + return "https://" + self.host + self.api_path.format(self.store_hash, version, url) + return "https://" + self.host + self.api_path.format(self.store_hash, version, url) @staticmethod def _oauth_headers(cid, atoken): @@ -286,4 +294,4 @@ def _handle_response(self, url, res, suppress_empty=True): else: callback() - return result + return result \ No newline at end of file diff --git a/bigcommerce/resources/__init__.py b/bigcommerce/resources/__init__.py index 541fc4f..466b1e2 100644 --- a/bigcommerce/resources/__init__.py +++ b/bigcommerce/resources/__init__.py @@ -1,23 +1 @@ -from .banners import * -from .blog_posts import * -from .brands import * -from .categories import * -from .countries import * -from .coupons import * -from .currencies import * -from .customer_groups import * -from .customers import * -from .gift_certificates import * -from .option_sets import * -from .options import * -from .order_statuses import * -from .orders import * -from .pages import * -from .payments import * -from .products import * -from .redirects import * -from .shipping import * -from .store import * -from .tax_classes import * -from .time import * -from .webhooks import * +from .v2 import * \ No newline at end of file diff --git a/bigcommerce/resources/base.py b/bigcommerce/resources/base.py index 9dfec22..f0cc793 100644 --- a/bigcommerce/resources/base.py +++ b/bigcommerce/resources/base.py @@ -24,7 +24,10 @@ def __str__(self): """ Display as a normal dict, but filter out underscored items first """ - return str({k: self.__dict__[k] for k in self.__dict__ if not k.startswith("_")}) + return str({k: self.__dict__[k] for k in self.__dict__ if not k.startswith("_") and k.startswith('connection')}) + + def __json__(self): + return {k: self.__dict__[k] for k in self.__dict__ if not k.startswith("_") and not k.startswith('connection')} def __repr__(self): return "<%s at %s, %s>" % (type(self).__name__, hex(id(self)), str(self)) @@ -32,6 +35,7 @@ def __repr__(self): class ApiResource(Mapping): resource_name = "" # The identifier which describes this resource in urls + resource_version = "v2" @classmethod def _create_object(cls, response, connection=None): @@ -42,34 +46,68 @@ def _create_object(cls, response, connection=None): @classmethod def _make_request(cls, method, url, connection, data=None, params=None, headers=None): - return connection.make_request(method, url, data, params, headers) + return connection.make_request(method, url, data, params, headers, version=cls.resource_version) @classmethod def _get_path(cls, id): - return "%s/%s" % (cls.resource_name, id) + if id: + return "%s/%s" % (cls.resource_name, id) + else: + return "%s" % (cls.resource_name) @classmethod - def get(cls, id, connection=None, **params): + def get(cls, id=None, connection=None, **params): response = cls._make_request('GET', cls._get_path(id), connection, params=params) return cls._create_object(response, connection=connection) class ApiSubResource(ApiResource): + gparent_resource = "" + parent_resource = "" + gparent_key = "" + parent_key = "" + + @classmethod + def _get_path(cls, parentid, id=None): + if id: + return "%s/%s/%s/%s" % (cls.parent_resource, parentid, cls.resource_name, id) + return "%s/%s/%s" % (cls.parent_resource, parentid, cls.resource_name) + + @classmethod + def get(cls, parentid, id=None, connection=None, **params): + response = cls._make_request('GET', cls._get_path(parentid, id), connection, params=params) + return cls._create_object(response, connection=connection) + + def parent_id(self): + return self[self.parent_key] + + def gparent_id(self): + return self[self.gparent_key] + + +class ApiSubSubResource(ApiSubResource): + gparent_resource = "" parent_resource = "" + gparent_key = "" parent_key = "" @classmethod - def _get_path(cls, id, parentid): - return "%s/%s/%s/%s" % (cls.parent_resource, parentid, cls.resource_name, id) + def _get_path(cls, gparentid, parentid, id=None): + if id: + return "%s/%s/%s/%s/%s/%s" % (cls.gparent_resource, gparentid, cls.parent_resource, parentid, cls.resource_name, id) + return "%s/%s/%s/%s/%s" % (cls.gparent_resource, gparentid, cls.parent_resource, parentid, cls.resource_name) @classmethod - def get(cls, parentid, id, connection=None, **params): - response = cls._make_request('GET', cls._get_path(id, parentid), connection, params=params) + def get(cls, gparentid, parentid, id=None, connection=None, **params): + response = cls._make_request('GET', cls._get_path(gparentid, parentid, id), connection, params=params) return cls._create_object(response, connection=connection) def parent_id(self): return self[self.parent_key] + def gparent_id(self): + return self[self.gparent_key] + class CreateableApiResource(ApiResource): @classmethod @@ -93,6 +131,17 @@ def create(cls, parentid, connection=None, **params): return cls._create_object(response, connection=connection) +class CreateableApiSubSubResource(ApiSubSubResource): + @classmethod + def _create_path(cls, gparentid, parentid): + return "%s/%s/%s/%s/%s" % (cls.gparent_resource, gparentid, cls.parent_resource, parentid, cls.resource_name) + + @classmethod + def create(cls, gparentid, parentid, connection=None, **params): + response = cls._make_request('POST', cls._create_path(gparentid, parentid), connection, data=params) + return cls._create_object(response, connection=connection) + + class ListableApiResource(ApiResource): @classmethod def _get_all_path(cls): @@ -103,7 +152,6 @@ def all(cls, connection=None, **params): """ Returns first page if no params passed in as a list. """ - request = cls._make_request('GET', cls._get_all_path(), connection, params=params) return cls._create_object(request, connection=connection) @@ -150,7 +198,7 @@ def _all_responses(): class ListableApiSubResource(ApiSubResource): @classmethod - def _get_all_path(cls, parentid=None): + def _get_all_path(cls, parentid): # Not all sub resources require a parent id. Eg: /api/v2/products/skus?sku= if (parentid): return "%s/%s/%s" % (cls.parent_resource, parentid, cls.resource_name) @@ -163,6 +211,18 @@ def all(cls, parentid=None, connection=None, **params): return cls._create_object(response, connection=connection) +class ListableApiSubSubResource(ApiSubSubResource): + @classmethod + def _get_all_path(cls, gparentid, parentid): + # Not all sub resources require a parent id. Eg: /api/v2/products/skus?sku= + return "%s/%s/%s/%s/%s" % (cls.gparent_resource, gparentid, cls.parent_resource, parentid, cls.resource_name) + + @classmethod + def all(cls, gparentid, parentid, connection=None, **params): + response = cls._make_request('GET', cls._get_all_path(parentid, gparentid), connection, params=params) + return cls._create_object(response, connection=connection) + + class UpdateableApiResource(ApiResource): def _update_path(self): return "%s/%s" % (self.resource_name, self.id) @@ -171,6 +231,27 @@ def update(self, **updates): response = self._make_request('PUT', self._update_path(), self._connection, data=updates) return self._create_object(response, connection=self._connection) +class CollectionUpdateableApiResource(ApiResource): + @classmethod + def _update_path(cls): + return cls.resource_name + @classmethod + def update(cls, updates, connection=None): + response = cls._make_request('PUT', cls._update_path(), connection, updates) + return cls._create_object(response, connection=connection) + +class CollectionCreatableApiSubResource(ApiResource): + @classmethod + def _create_path(cls, parentid): + return "%s/%s/%s" % (cls.parent_resource, parentid, cls.resource_name) + + @classmethod + def create(cls, parentid, data, connection=None): + response = cls._make_request('POST', cls._create_path(parentid), connection, data) + return cls._create_object(response, connection=connection) + + +# TODO: Add Upsertable? class UpdateableApiSubResource(ApiSubResource): def _update_path(self): @@ -181,8 +262,19 @@ def update(self, **updates): return self._create_object(response, connection=self._connection) +class UpdateableApiSubSubResource(ApiSubResource): + def _update_path(self): + return "%s/%s/%s/%s/%s/%s" % (self.gparent_resource, self.gparent_id(), self.parent_resource, self.parent_id(), self.resource_name, self.id) + + def update(self, **updates): + response = self._make_request('PUT', self._update_path(), self._connection, data=updates) + return self._create_object(response, connection=self._connection) + + class DeleteableApiResource(ApiResource): def _delete_path(self): + if 'id' not in self and 'uuid' in self: # widgets have uuid not id + return "%s/%s" % (self.resource_name, self.uuid) return "%s/%s" % (self.resource_name, self.id) def delete(self): @@ -197,6 +289,14 @@ def delete(self): return self._make_request('DELETE', self._delete_path(), self._connection) +class DeleteableApiSubSubResource(ApiSubSubResource): + def _delete_path(self): + return "%s/%s/%s/%s/%s/%s" % (self.gparent_resource, self.gparent_id(), self.parent_resource, self.parent_id(), self.resource_name, self.id) + + def delete(self): + return self._make_request('DELETE', self._delete_path(), self._connection) + + class CollectionDeleteableApiResource(ApiResource): @classmethod def _delete_all_path(cls): @@ -216,7 +316,6 @@ def _delete_all_path(cls, parentid): def delete_all(cls, parentid, connection=None): return cls._make_request('DELETE', cls._delete_all_path(parentid), connection) - class CountableApiResource(ApiResource): @classmethod def _count_path(cls): @@ -245,4 +344,4 @@ def _count_path(cls, parentid=None): @classmethod def count(cls, parentid=None, connection=None, **params): response = cls._make_request('GET', cls._count_path(parentid), connection, params=params) - return response['count'] + return response['count'] \ No newline at end of file diff --git a/bigcommerce/resources/store.py b/bigcommerce/resources/store.py deleted file mode 100644 index 1a272af..0000000 --- a/bigcommerce/resources/store.py +++ /dev/null @@ -1,5 +0,0 @@ -from .base import * - - -class Store(ListableApiResource): - resource_name = 'store' diff --git a/bigcommerce/resources/v2/__init__.py b/bigcommerce/resources/v2/__init__.py new file mode 100644 index 0000000..1d2cb4d --- /dev/null +++ b/bigcommerce/resources/v2/__init__.py @@ -0,0 +1,23 @@ +from .banners import * +from .blog_posts import * +from .brands import * +from .categories import * +from .countries import * +from .coupons import * +from .currencies import * +from .customer_groups import * +from .customers import * +from .gift_certificates import * +from .option_sets import * +from .options import * +from .order_statuses import * +from .orders import * +from .pages import * +from .payments import * +from .products import * +from .redirects import * +from .shipping import * +from .store import * +from .tax_classes import * +from .time import * +from .webhooks import * \ No newline at end of file diff --git a/bigcommerce/resources/banners.py b/bigcommerce/resources/v2/banners.py similarity index 90% rename from bigcommerce/resources/banners.py rename to bigcommerce/resources/v2/banners.py index bbfd2fb..f0fcab4 100644 --- a/bigcommerce/resources/banners.py +++ b/bigcommerce/resources/v2/banners.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Banners(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/blog_posts.py b/bigcommerce/resources/v2/blog_posts.py similarity index 86% rename from bigcommerce/resources/blog_posts.py rename to bigcommerce/resources/v2/blog_posts.py index 11e0d31..9c7cd04 100644 --- a/bigcommerce/resources/blog_posts.py +++ b/bigcommerce/resources/v2/blog_posts.py @@ -1,7 +1,9 @@ -from .base import * +from ..base import * class BlogPosts(ListableApiResource, CreateableApiResource, UpdateableApiResource, DeleteableApiResource, CountableApiResource, CollectionDeleteableApiResource): resource_name = 'blog/posts' + +# TODO: tags \ No newline at end of file diff --git a/bigcommerce/resources/brands.py b/bigcommerce/resources/v2/brands.py similarity index 91% rename from bigcommerce/resources/brands.py rename to bigcommerce/resources/v2/brands.py index 91d7d9f..532d3da 100644 --- a/bigcommerce/resources/brands.py +++ b/bigcommerce/resources/v2/brands.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Brands(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/categories.py b/bigcommerce/resources/v2/categories.py similarity index 91% rename from bigcommerce/resources/categories.py rename to bigcommerce/resources/v2/categories.py index a96d634..7224d04 100644 --- a/bigcommerce/resources/categories.py +++ b/bigcommerce/resources/v2/categories.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Categories(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/countries.py b/bigcommerce/resources/v2/countries.py similarity index 95% rename from bigcommerce/resources/countries.py rename to bigcommerce/resources/v2/countries.py index db5baa1..dfc3e12 100644 --- a/bigcommerce/resources/countries.py +++ b/bigcommerce/resources/v2/countries.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Countries(ListableApiResource, CountableApiResource): diff --git a/bigcommerce/resources/coupons.py b/bigcommerce/resources/v2/coupons.py similarity index 91% rename from bigcommerce/resources/coupons.py rename to bigcommerce/resources/v2/coupons.py index 8c865ac..74025b2 100644 --- a/bigcommerce/resources/coupons.py +++ b/bigcommerce/resources/v2/coupons.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Coupons(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/currencies.py b/bigcommerce/resources/v2/currencies.py similarity index 88% rename from bigcommerce/resources/currencies.py rename to bigcommerce/resources/v2/currencies.py index 31627a5..b31725a 100644 --- a/bigcommerce/resources/currencies.py +++ b/bigcommerce/resources/v2/currencies.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Currencies(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/customer_groups.py b/bigcommerce/resources/v2/customer_groups.py similarity index 92% rename from bigcommerce/resources/customer_groups.py rename to bigcommerce/resources/v2/customer_groups.py index e574c6a..ff20971 100644 --- a/bigcommerce/resources/customer_groups.py +++ b/bigcommerce/resources/v2/customer_groups.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class CustomerGroups(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/customers.py b/bigcommerce/resources/v2/customers.py similarity index 97% rename from bigcommerce/resources/customers.py rename to bigcommerce/resources/v2/customers.py index d68ba4d..d5b7ed3 100644 --- a/bigcommerce/resources/customers.py +++ b/bigcommerce/resources/v2/customers.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Customers(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/gift_certificates.py b/bigcommerce/resources/v2/gift_certificates.py similarity index 91% rename from bigcommerce/resources/gift_certificates.py rename to bigcommerce/resources/v2/gift_certificates.py index 30938f6..2eb5187 100644 --- a/bigcommerce/resources/gift_certificates.py +++ b/bigcommerce/resources/v2/gift_certificates.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class GiftCertificates(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/option_sets.py b/bigcommerce/resources/v2/option_sets.py similarity index 97% rename from bigcommerce/resources/option_sets.py rename to bigcommerce/resources/v2/option_sets.py index 1da826b..ea02e9c 100644 --- a/bigcommerce/resources/option_sets.py +++ b/bigcommerce/resources/v2/option_sets.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class OptionSets(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/options.py b/bigcommerce/resources/v2/options.py similarity index 97% rename from bigcommerce/resources/options.py rename to bigcommerce/resources/v2/options.py index 5cd003b..617c2ff 100644 --- a/bigcommerce/resources/options.py +++ b/bigcommerce/resources/v2/options.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Options(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/order_statuses.py b/bigcommerce/resources/v2/order_statuses.py similarity index 79% rename from bigcommerce/resources/order_statuses.py rename to bigcommerce/resources/v2/order_statuses.py index 4179559..ab01fbe 100644 --- a/bigcommerce/resources/order_statuses.py +++ b/bigcommerce/resources/v2/order_statuses.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class OrderStatuses(ListableApiResource): diff --git a/bigcommerce/resources/orders.py b/bigcommerce/resources/v2/orders.py similarity index 99% rename from bigcommerce/resources/orders.py rename to bigcommerce/resources/v2/orders.py index 2badc9c..b0b1b96 100644 --- a/bigcommerce/resources/orders.py +++ b/bigcommerce/resources/v2/orders.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Orders(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/pages.py b/bigcommerce/resources/v2/pages.py similarity index 91% rename from bigcommerce/resources/pages.py rename to bigcommerce/resources/v2/pages.py index d0cdca1..cd007cf 100644 --- a/bigcommerce/resources/pages.py +++ b/bigcommerce/resources/v2/pages.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Pages(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/payments.py b/bigcommerce/resources/v2/payments.py similarity index 80% rename from bigcommerce/resources/payments.py rename to bigcommerce/resources/v2/payments.py index 10cd6b8..d694915 100644 --- a/bigcommerce/resources/payments.py +++ b/bigcommerce/resources/v2/payments.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class PaymentMethods(ListableApiResource): diff --git a/bigcommerce/resources/products.py b/bigcommerce/resources/v2/products.py similarity index 99% rename from bigcommerce/resources/products.py rename to bigcommerce/resources/v2/products.py index d569de9..fbfa2b1 100644 --- a/bigcommerce/resources/products.py +++ b/bigcommerce/resources/v2/products.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Products(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/redirects.py b/bigcommerce/resources/v2/redirects.py similarity index 91% rename from bigcommerce/resources/redirects.py rename to bigcommerce/resources/v2/redirects.py index ceed52c..8ba28c0 100644 --- a/bigcommerce/resources/redirects.py +++ b/bigcommerce/resources/v2/redirects.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Redirects(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/shipping.py b/bigcommerce/resources/v2/shipping.py similarity index 60% rename from bigcommerce/resources/shipping.py rename to bigcommerce/resources/v2/shipping.py index 4df1b04..bdca8c0 100644 --- a/bigcommerce/resources/shipping.py +++ b/bigcommerce/resources/v2/shipping.py @@ -1,5 +1,8 @@ -from .base import * +from ..base import * class ShippingMethods(ListableApiResource): resource_name = 'shipping/methods' + + +# TODO: carriers, methods, zones \ No newline at end of file diff --git a/bigcommerce/resources/v2/store.py b/bigcommerce/resources/v2/store.py new file mode 100644 index 0000000..49a2627 --- /dev/null +++ b/bigcommerce/resources/v2/store.py @@ -0,0 +1,5 @@ +from ..base import * + + +class Store(ApiResource): + resource_name = 'store' diff --git a/bigcommerce/resources/tax_classes.py b/bigcommerce/resources/v2/tax_classes.py similarity index 78% rename from bigcommerce/resources/tax_classes.py rename to bigcommerce/resources/v2/tax_classes.py index 48dc391..61285e2 100644 --- a/bigcommerce/resources/tax_classes.py +++ b/bigcommerce/resources/v2/tax_classes.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class TaxClasses(ListableApiResource): diff --git a/bigcommerce/resources/time.py b/bigcommerce/resources/v2/time.py similarity index 74% rename from bigcommerce/resources/time.py rename to bigcommerce/resources/v2/time.py index 3f92df9..35960d3 100644 --- a/bigcommerce/resources/time.py +++ b/bigcommerce/resources/v2/time.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Time(ListableApiResource): diff --git a/bigcommerce/resources/webhooks.py b/bigcommerce/resources/v2/webhooks.py similarity index 87% rename from bigcommerce/resources/webhooks.py rename to bigcommerce/resources/v2/webhooks.py index a8e5137..2437f2d 100644 --- a/bigcommerce/resources/webhooks.py +++ b/bigcommerce/resources/v2/webhooks.py @@ -1,4 +1,4 @@ -from .base import * +from ..base import * class Webhooks(ListableApiResource, CreateableApiResource, diff --git a/bigcommerce/resources/v3/__init__.py b/bigcommerce/resources/v3/__init__.py new file mode 100644 index 0000000..6073602 --- /dev/null +++ b/bigcommerce/resources/v3/__init__.py @@ -0,0 +1,29 @@ +from .abandoned_cart_emails import * +from .abandoned_carts import * +from .api_tokens import * +from .brands import * +from .carts import * +from .catalog_summary import * +from .categories import * +from .channels import * +from .checkouts import * +from .customers import * +from .custom_template_associations import * +from .email_templates import * +from .orders import * +from .pages import * +from .payments import * +from .pricelists import * +from .pricing import * +from .products import * +from .redirects import * +from .scripts import * +from .settings import * +from .sites import * +from .shipping import * +from .tax import * +from .themes import * +from .variants import * +from .webhooks import * +from .widgets import * +from .wishlists import * \ No newline at end of file diff --git a/bigcommerce/resources/v3/abandoned_cart_emails.py b/bigcommerce/resources/v3/abandoned_cart_emails.py new file mode 100644 index 0000000..7bd5741 --- /dev/null +++ b/bigcommerce/resources/v3/abandoned_cart_emails.py @@ -0,0 +1,16 @@ +from ..base import * + + +class AbandonedCartEmails(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'marketing/email-templates/abandoned_cart_email' + + def default(self): + return AbandonedCartEmailsDefault.all(connection=self._connection) + + +class AbandonedCartEmailsDefault(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'marketing/abandoned-cart-emails/default' diff --git a/bigcommerce/resources/v3/abandoned_carts.py b/bigcommerce/resources/v3/abandoned_carts.py new file mode 100644 index 0000000..1d7e163 --- /dev/null +++ b/bigcommerce/resources/v3/abandoned_carts.py @@ -0,0 +1,15 @@ +from ..base import * + + +class AbandonedCarts(ListableApiResource): + resource_version = 'v3' + resource_name = 'abandoned-carts' + +#TODO +""" +GET /stores/{store_hash}/v3/abandoned-carts/settings +PUT /stores/{store_hash}/v3/abandoned-carts/settings +GET /stores/{store_hash}/v3/abandoned-carts/settings/channels/{channel_id} +PUT /stores/{store_hash}/v3/abandoned-carts/settings/channels/{channel_id} +GET /stores/{store_hash}/v3/abandoned-carts/{token} +""" \ No newline at end of file diff --git a/bigcommerce/resources/v3/api_tokens.py b/bigcommerce/resources/v3/api_tokens.py new file mode 100644 index 0000000..7430edb --- /dev/null +++ b/bigcommerce/resources/v3/api_tokens.py @@ -0,0 +1,11 @@ +from ..base import * + + +class ApiToken(CreateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'storefront/api-token' + + +class ApiTokenCustomerImpersonation(CreateableApiResource): + resource_version = 'v3' + resource_name = 'storefront/api-token-customer-impersonation' diff --git a/bigcommerce/resources/v3/brands.py b/bigcommerce/resources/v3/brands.py new file mode 100644 index 0000000..475d0f2 --- /dev/null +++ b/bigcommerce/resources/v3/brands.py @@ -0,0 +1,34 @@ +from ..base import * + + +class Brands(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, + CollectionDeleteableApiResource, CountableApiResource): + resource_version = 'v3' + resource_name = 'catalog/brands' + + def images(self, id=None): + if id: + return BrandImages.get(self.id, id, connection=self._connection) + else: + return BrandImages.all(self.id, connection=self._connection) + + def metafields(self, id=None): + if id: + return BrandMetafields.get(self.id, id, connection=self._connection) + else: + return BrandMetafields.all(self.id, connection=self._connection) + + +class BrandImages(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Brands.resource_version + resource_name = 'XXX' + parent_resource = Brands.resource_name + parent_key = 'brand_id' + + +class BrandMetafields(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Brands.resource_version + resource_name = 'XXX' + parent_resource = Brands.resource_name + parent_key = 'brand_id' diff --git a/bigcommerce/resources/v3/carts.py b/bigcommerce/resources/v3/carts.py new file mode 100644 index 0000000..361f664 --- /dev/null +++ b/bigcommerce/resources/v3/carts.py @@ -0,0 +1,11 @@ +from ..base import * + +# TODO: test +class Carts(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, + CollectionDeleteableApiResource, CountableApiResource): + resource_version = 'v3' + resource_name = 'carts' + +# TODO: carts/{cartId}/items, post put delete +# TODO: carts/{cartId}/redirect_urls, post diff --git a/bigcommerce/resources/v3/catalog_summary.py b/bigcommerce/resources/v3/catalog_summary.py new file mode 100644 index 0000000..64b395e --- /dev/null +++ b/bigcommerce/resources/v3/catalog_summary.py @@ -0,0 +1 @@ +# TODO: this \ No newline at end of file diff --git a/bigcommerce/resources/v3/categories.py b/bigcommerce/resources/v3/categories.py new file mode 100644 index 0000000..f3a5bd9 --- /dev/null +++ b/bigcommerce/resources/v3/categories.py @@ -0,0 +1,13 @@ +from ..base import * + + +class Categories(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, + CollectionDeleteableApiResource, CountableApiResource): + resource_version = 'v3' + resource_name = 'catalog/categories' + + +# TODO: product sort order +# TODO: images +# TODO: metafields \ No newline at end of file diff --git a/bigcommerce/resources/v3/channels.py b/bigcommerce/resources/v3/channels.py new file mode 100644 index 0000000..b52adac --- /dev/null +++ b/bigcommerce/resources/v3/channels.py @@ -0,0 +1,13 @@ +from ..base import * + + +class Channels(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, + CollectionDeleteableApiResource, CountableApiResource): + resource_version = 'v3' + resource_name = 'channels' + + +# TODO: channels/{channel_id}/active-theme +# channels/currency-assignments +# channels/{channel_id}/listings \ No newline at end of file diff --git a/bigcommerce/resources/v3/checkouts.py b/bigcommerce/resources/v3/checkouts.py new file mode 100644 index 0000000..7a4cc25 --- /dev/null +++ b/bigcommerce/resources/v3/checkouts.py @@ -0,0 +1,8 @@ +from ..base import * + + +class Checkouts(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'checkouts' + +# 9139f12e-053f-4dfc-ba8b-0e6ec4a4e4a2 \ No newline at end of file diff --git a/bigcommerce/resources/v3/custom_template_associations.py b/bigcommerce/resources/v3/custom_template_associations.py new file mode 100644 index 0000000..7f8bbd9 --- /dev/null +++ b/bigcommerce/resources/v3/custom_template_associations.py @@ -0,0 +1,6 @@ +from ..base import * + +# TODO: Test +class CustomTemplateAssociations(DeleteableApiResource, CollectionDeleteableApiResource, CollectionUpdateableApiResource): + resource_version = 'v3' + resource_name = 'storefront/custom-template-associations' \ No newline at end of file diff --git a/bigcommerce/resources/v3/customers.py b/bigcommerce/resources/v3/customers.py new file mode 100644 index 0000000..bc14932 --- /dev/null +++ b/bigcommerce/resources/v3/customers.py @@ -0,0 +1,21 @@ +from ..base import * + + +class Customers(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, + CollectionDeleteableApiResource, CountableApiResource): + + resource_name = 'customers' + resource_version = 'v3' + +class CustomerAddresses(ListableApiResource, CreateableApiResource, CollectionUpdateableApiResource, DeleteableApiResource): + + resource_name = 'customers/addresses' + resource_version = 'v3' + +class CustomerFormFieldValues(ListableApiResource, UpdateableApiResource): + + resource_name = 'customers/form-field-values' + resource_version = 'v3' + +# TODO: subscribers \ No newline at end of file diff --git a/bigcommerce/resources/v3/email_templates.py b/bigcommerce/resources/v3/email_templates.py new file mode 100644 index 0000000..c755522 --- /dev/null +++ b/bigcommerce/resources/v3/email_templates.py @@ -0,0 +1,8 @@ +from ..base import * + + +# TODO: test +class EmailTemplates(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'marketing/email-templates' \ No newline at end of file diff --git a/bigcommerce/resources/v3/orders.py b/bigcommerce/resources/v3/orders.py new file mode 100644 index 0000000..fccc9c7 --- /dev/null +++ b/bigcommerce/resources/v3/orders.py @@ -0,0 +1,43 @@ +from ..base import * + +# TODO: test +class OrderMetafields(ListableApiSubResource, CreateableApiSubResource, UpdateableApiSubResource, DeleteableApiSubResource): + resource_version = 'v3' + resource_name = 'metafields' + parent_resource = 'orders' + parent_key = 'order_id' + + +class OrderPaymentActionsCapture(CreateableApiSubResource): + resource_version = 'v3' + resource_name = 'payment_actions/capture' + parent_resource = 'orders' + parent_key = 'order_id' + + +class OrderPaymentActionsRefundQuotes(CollectionCreatableApiSubResource): + resource_version = 'v3' + resource_name = 'payment_actions/refund_quotes' + parent_resource = 'orders' + parent_key = 'order_id' + + +class OrderPaymentActionsRefunds(ListableApiSubResource, CreateableApiSubResource, UpdateableApiSubResource, DeleteableApiSubResource): + resource_version = 'v3' + resource_name = 'payment_actions/refunds' + parent_resource = 'orders' + parent_key = 'order_id' + + +class OrderPaymentActionsVoid(CreateableApiSubResource): + resource_version = 'v3' + resource_name = 'payment_actions/void' + parent_resource = 'orders' + parent_key = 'order_id' + + +class OrderTransactions(ListableApiSubResource): + resource_version = 'v3' + resource_name = 'transactions' + parent_resource = 'orders' + parent_key = 'order_id' \ No newline at end of file diff --git a/bigcommerce/resources/v3/pages.py b/bigcommerce/resources/v3/pages.py new file mode 100644 index 0000000..2ad0422 --- /dev/null +++ b/bigcommerce/resources/v3/pages.py @@ -0,0 +1,9 @@ +from ..base import * + + +# TODO: test +# TODO: add CollectionUpdateableApiResource +class Pages(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, CollectionDeleteableApiResource): + resource_version = 'v3' + resource_name = 'content/pages' \ No newline at end of file diff --git a/bigcommerce/resources/v3/payments.py b/bigcommerce/resources/v3/payments.py new file mode 100644 index 0000000..64b395e --- /dev/null +++ b/bigcommerce/resources/v3/payments.py @@ -0,0 +1 @@ +# TODO: this \ No newline at end of file diff --git a/bigcommerce/resources/v3/pricelists.py b/bigcommerce/resources/v3/pricelists.py new file mode 100644 index 0000000..73ef806 --- /dev/null +++ b/bigcommerce/resources/v3/pricelists.py @@ -0,0 +1,28 @@ +from ..base import * + + +# TODO: test +class Pricelists(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'pricelists' + + +# TODO: test +class PricelistAssignments(ListableApiResource, CreateableApiResource, DeleteableApiResource): + resource_version = Pricelists.resource_version + resource_name = 'assignments' + parent_resource = Pricelists.resource_name + parent_key = 'pricelist_id' + + +# TODO: test +class PricelistRecords(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = Pricelists.resource_version + resource_name = 'records' + parent_resource = Pricelists.resource_name + parent_key = 'pricelist_id' + + # TODO: get by variant + # TODO: get by currency \ No newline at end of file diff --git a/bigcommerce/resources/v3/pricing.py b/bigcommerce/resources/v3/pricing.py new file mode 100644 index 0000000..4fa0007 --- /dev/null +++ b/bigcommerce/resources/v3/pricing.py @@ -0,0 +1,7 @@ +from ..base import * + + +# TODO: test +class PricingProducts(CreateableApiResource): + resource_version = 'v3' + resource_name = 'pricing/products' diff --git a/bigcommerce/resources/v3/products.py b/bigcommerce/resources/v3/products.py new file mode 100644 index 0000000..fb4a1a5 --- /dev/null +++ b/bigcommerce/resources/v3/products.py @@ -0,0 +1,163 @@ +from ..base import * + + +class Products(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource, + CollectionDeleteableApiResource, CountableApiResource): + resource_version = 'v3' + resource_name = 'catalog/products' + + def bulk_pricing_rules(self, id=None): + if id: + return ProductBulkPricingRules.get(self.id, id, connection=self._connection) + else: + return ProductBulkPricingRules.all(self.id, connection=self._connection) + + def complex_rules(self, id=None): + if id: + return ProductComplexRules.get(self.id, id, connection=self._connection) + else: + return ProductComplexRules.all(self.id, connection=self._connection) + + def custom_fields(self, id=None): + if id: + return ProductCustomFields.get(self.id, id, connection=self._connection) + else: + return ProductCustomFields.all(self.id, connection=self._connection) + + def images(self, id=None): + if id: + return ProductImages.get(self.id, id, connection=self._connection) + else: + return ProductImages.all(self.id, connection=self._connection) + + def metafields(self, id=None): + if id: + return ProductMetafields.get(self.id, id, connection=self._connection) + else: + return ProductMetafields.all(self.id, connection=self._connection) + + def modifiers(self, id=None): + if id: + return ProductModifiers.get(self.id, id, connection=self._connection) + else: + return ProductModifiers.all(self.id, connection=self._connection) + + def videos(self, id=None): + if id: + return ProductVideos.get(self.id, id, connection=self._connection) + else: + return ProductVideos.all(self.id, connection=self._connection) + + +class ProductBulkPricingRules(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'XXX' + parent_resource = Products.resource_name + key = 'product_id' + + +class ProductComplexRules(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'XXX' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductCustomFields(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'XXX' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductImages(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'XXX' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductMetafields(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'XXX' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductModifiers(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'modifiers' + parent_resource = Products.resource_name + parent_key = 'product_id' + + def values(self, id=None): + if id: + return ProductModifiersValues.get(self.gparent_id(), self.parent_id(), id, connection=self._connection) + else: + return ProductModifiersValues.all(self.gparent_id(), self.parent_id(), connection=self._connection) + + +class ProductModifiersValues(ListableApiSubSubResource, CreateableApiSubSubResource, DeleteableApiSubSubResource): + resource_version = Products.resource_version + resource_name = 'values' + parent_resource = 'modifiers' + parent_key = 'modifier_id' + gparent_key = 'product_id' + gparent_resource=Products.resource_name + + +class ProductOptions(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'options' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductOptionsValues(ListableApiSubSubResource, CreateableApiSubSubResource, DeleteableApiSubSubResource): + resource_version = Products.resource_version + resource_name = 'values' + parent_resource = 'options' + parent_key = 'option_id' + gparent_key = 'product_id' + gparent_resource=Products.resource_name + +class ProductReviews(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'XXX' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductVariants(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Products.resource_version + resource_name = 'variants' + parent_resource = Products.resource_name + parent_key = 'product_id' + + +class ProductVariantsImages(ListableApiSubSubResource, CreateableApiSubSubResource, DeleteableApiSubSubResource): + resource_version = Products.resource_version + resource_name = 'images' + parent_resource = 'variants' + parent_key = 'variant_id' + gparent_key = 'product_id' + gparent_resource=Products.resource_name + + +class ProductVariantsMetafields(ListableApiSubSubResource, CreateableApiSubSubResource, DeleteableApiSubSubResource): + resource_version = Products.resource_version + resource_name = 'metafields' + parent_resource = 'variants' + parent_key = 'variant_id' + gparent_key = 'product_id' + gparent_resource=Products.resource_name + + +class ProductVideos(ListableApiSubResource, CountableApiSubResource, + CreateableApiSubResource, DeleteableApiSubResource): + resource_version = 'v3' + resource_name = 'videos' + parent_resource = 'catalog/products' + parent_key = 'product_id' + count_resource = 'products/videos' diff --git a/bigcommerce/resources/v3/redirects.py b/bigcommerce/resources/v3/redirects.py new file mode 100644 index 0000000..f7f9b87 --- /dev/null +++ b/bigcommerce/resources/v3/redirects.py @@ -0,0 +1,7 @@ +from ..base import * + + +class Redirects(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'storefront/redirects' \ No newline at end of file diff --git a/bigcommerce/resources/v3/scripts.py b/bigcommerce/resources/v3/scripts.py new file mode 100644 index 0000000..91dae5a --- /dev/null +++ b/bigcommerce/resources/v3/scripts.py @@ -0,0 +1,7 @@ +from ..base import * + + +class Scripts(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'content/scripts' \ No newline at end of file diff --git a/bigcommerce/resources/v3/settings.py b/bigcommerce/resources/v3/settings.py new file mode 100644 index 0000000..ec2d5a4 --- /dev/null +++ b/bigcommerce/resources/v3/settings.py @@ -0,0 +1,95 @@ +from ..base import * + +class SettingsAnalytics(ListableApiResource, UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/analytics' + + +class SettingsEmailStatuses(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/email-statuses' + + +class SettingsFaviconImage(CreateableApiResource): + resource_version = 'v3' + resource_name = 'settings/favicon/image' + +class SettingsCatalog(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/catalog' + + +class SettingsInventoryNotifications(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/inventory/notifications' + + +class SettingsLogo(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/logo' + + +class SettingsLogoImage(CreateableApiResource): + resource_version = 'v3' + resource_name = 'settings/logo/image' + + +class SettingsStorefrontProduct(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/product' + + +class SettingsSearchFilters(ListableApiResource, + CollectionUpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'settings/search/filters' + + +class SettingsSearchFiltersAvailable(ListableApiResource): + resource_version = 'v3' + resource_name = 'settings/search/filters/available' + + +class SettingsSearchFiltersContexts(ListableApiResource, UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/search/filters/contexts' + + +class SettingsStoreLocale(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/store/locale' + + +class SettingsStorefrontCategory(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/category' + + +class SettingsStorefrontRobotstxt(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/robotstxt' + + +class SettingsStorefrontSearch(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/search' + + +class SettingsStorefrontSecurity(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/security' + + +class SettingsStorefrontSeo(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/seo' + + +class SettingsStorefrontStatus(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/storefront/status' + + +class SettingsStoreProfile(UpdateableApiResource): + resource_version = 'v3' + resource_name = 'settings/store/profile' \ No newline at end of file diff --git a/bigcommerce/resources/v3/shipping.py b/bigcommerce/resources/v3/shipping.py new file mode 100644 index 0000000..77bb99d --- /dev/null +++ b/bigcommerce/resources/v3/shipping.py @@ -0,0 +1,10 @@ +from ..base import * + + +# TODO: test +class ShippingProductsCustomsInformation(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'shipping/products/customs-information' + + # TODO: add subresource function diff --git a/bigcommerce/resources/v3/sites.py b/bigcommerce/resources/v3/sites.py new file mode 100644 index 0000000..dd10cbb --- /dev/null +++ b/bigcommerce/resources/v3/sites.py @@ -0,0 +1,18 @@ +from ..base import * + + +# TODO: test +class Sites(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'sites' + + # TODO: add subresource function + + +# TODO: test +class SiteRoutes(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Sites.resource_version + resource_name = 'routes' + parent_resource = Sites.resource_name + parent_key = 'site_id' diff --git a/bigcommerce/resources/v3/tax.py b/bigcommerce/resources/v3/tax.py new file mode 100644 index 0000000..64b395e --- /dev/null +++ b/bigcommerce/resources/v3/tax.py @@ -0,0 +1 @@ +# TODO: this \ No newline at end of file diff --git a/bigcommerce/resources/v3/themes.py b/bigcommerce/resources/v3/themes.py new file mode 100644 index 0000000..aa0cf7c --- /dev/null +++ b/bigcommerce/resources/v3/themes.py @@ -0,0 +1,36 @@ +from ..base import * + +# TODO: Test + +class Themes(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'themes' + +class CustomTemplates(ListableApiResource, CreateableApiResource, + CollectionUpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'themes/custom-templates' + key='version_uuid' + +class ThemeActions(CreateableApiSubResource): + resource_version = Themes.resource_version + resource_name = 'actions' + parent_resource = Themes.resource_name + key = 'theme_uuid' + + +# TODO: Test +class ThemeActionsDownload(CreateableApiSubResource): + resource_version = Themes.resource_version + resource_name = 'actions/download' + parent_resource = Themes.resource_name + key = 'theme_uuid' + + +# TODO: Test +class ThemeActionsActivate(CreateableApiSubResource): + resource_version = Themes.resource_version + resource_name = 'actions/activate' + parent_resource = Themes.resource_name + key = 'theme_uuid' \ No newline at end of file diff --git a/bigcommerce/resources/v3/variants.py b/bigcommerce/resources/v3/variants.py new file mode 100644 index 0000000..64b395e --- /dev/null +++ b/bigcommerce/resources/v3/variants.py @@ -0,0 +1 @@ +# TODO: this \ No newline at end of file diff --git a/bigcommerce/resources/v3/webhooks.py b/bigcommerce/resources/v3/webhooks.py new file mode 100644 index 0000000..2f4a7a3 --- /dev/null +++ b/bigcommerce/resources/v3/webhooks.py @@ -0,0 +1,7 @@ +from ..base import * + + +class Webhooks(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version='v3' + resource_name = 'hooks' \ No newline at end of file diff --git a/bigcommerce/resources/v3/widgets.py b/bigcommerce/resources/v3/widgets.py new file mode 100644 index 0000000..d3e669e --- /dev/null +++ b/bigcommerce/resources/v3/widgets.py @@ -0,0 +1,24 @@ +from ..base import * + + +class Widgets(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'content/widgets' + + +class WidgetTemplates(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'content/widget-templates' + + +class WidgetRegions(ListableApiResource): + resource_version = 'v3' + resource_name = 'content/regions' + + +class WidgetPlacements(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'content/placements' \ No newline at end of file diff --git a/bigcommerce/resources/v3/wishlists.py b/bigcommerce/resources/v3/wishlists.py new file mode 100644 index 0000000..896525e --- /dev/null +++ b/bigcommerce/resources/v3/wishlists.py @@ -0,0 +1,16 @@ +from ..base import * + + +# TODO: Test +class Wishlists(ListableApiResource, CreateableApiResource, + UpdateableApiResource, DeleteableApiResource): + resource_version = 'v3' + resource_name = 'wishlists' + + +# TODO: Test +class WishlistItems(ListableApiSubResource, CreateableApiSubResource, DeleteableApiSubResource): + resource_version = Wishlists.resource_version + resource_name = 'items' + parent_resource = Wishlists.resource_name + parent_key = 'wishlist_id' \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..62032d1 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1 @@ +sonar.projectKey=aglensmith_bigcommerce-api-python_AY1CCzYcg9ty7uGlFgnb \ No newline at end of file diff --git a/tests/test_base.py b/tests/test_base.py index 58b1ee0..f67de5a 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,8 +1,12 @@ +from sqlite3 import connect import unittest from bigcommerce.resources import Mapping, Orders, ApiResource, OrderShipments, Products, CountryStates,\ OrderCoupons, Webhooks, GoogleProductSearchMappings +from bigcommerce.resources.v3 import ProductModifiers, ProductModifiersValues from mock import MagicMock +from bigcommerce.resources.v3.abandoned_cart_emails import AbandonedCartEmails + class TestMapping(unittest.TestCase): def test_init(self): @@ -45,7 +49,7 @@ def test_get(self): self.assertIsInstance(result, Orders) self.assertEqual(result.id, 1) - connection.make_request.assert_called_once_with('GET', 'orders/1', None, {}, None) + connection.make_request.assert_called_once_with('GET', 'orders/1', None, {}, None, version='v2') class TestApiSubResource(unittest.TestCase): @@ -57,7 +61,27 @@ def test_get(self): self.assertIsInstance(result, OrderCoupons) self.assertEqual(result.id, 2) - connection.make_request.assert_called_once_with('GET', 'orders/1/coupons/2', None, {}, None) + connection.make_request.assert_called_once_with('GET', 'orders/1/coupons/2', None, {}, None, version='v2') + + def test_get_product_modifier(self): + connection = MagicMock() + connection.make_request.return_value = {'id': 2} + + result = ProductModifiers.get(1, 2, connection) + self.assertIsInstance(result, ProductModifiers) + self.assertEqual(result.id, 2) + + connection.make_request.assert_called_once_with('GET', 'catalog/products/1/modifiers/2', None, {}, None, version='v3') + + def test_get_product_modifier_value(self): + connection = MagicMock() + connection.make_request.return_value = {'id': 2} + + result = ProductModifiersValues.get(1, 2, 3, connection) + self.assertIsInstance(result, ProductModifiersValues) + self.assertEqual(result.id, 2) + + connection.make_request.assert_called_once_with('GET', 'catalog/products/1/modifiers/2/values/3', None, {}, None, version='v3') def test_parent_id(self): coupon = OrderCoupons({'id': 2, 'order_id': 1}) @@ -72,7 +96,7 @@ def test_create(self): result = Orders.create(connection, name="Hello") self.assertIsInstance(result, Orders) self.assertEqual(result.id, 1) - connection.make_request.assert_called_once_with('POST', 'orders', {'name': 'Hello'}, None, None) + connection.make_request.assert_called_once_with('POST', 'orders', {'name': 'Hello'}, None, None, version='v2') class TestCreateableApiSubResource(unittest.TestCase): @@ -83,7 +107,7 @@ def test_create(self): result = OrderShipments.create(1, connection, name="Hello") self.assertIsInstance(result, OrderShipments) self.assertEqual(result.id, 2) - connection.make_request.assert_called_once_with('POST', 'orders/1/shipments', {'name': 'Hello'}, None, None) + connection.make_request.assert_called_once_with('POST', 'orders/1/shipments', {'name': 'Hello'}, None, None, version='v2') class TestListableApiResource(unittest.TestCase): @@ -93,7 +117,12 @@ def test_all(self): result = Orders.all(connection, limit=3) self.assertEqual(len(list(result)), 3) - connection.make_request.assert_called_once_with('GET', 'orders', None, {'limit': 3}, None) + connection.make_request.assert_called_once_with('GET', 'orders', None, {'limit': 3}, None, version='v2') + + def test_abandoned_cart_emails_templates(self): + connection = MagicMock() + result = AbandonedCartEmails.all(connection) + connection.make_request.assert_called_once_with('GET', 'settings/marketing/abandoned-cart-emails', None, {}, None, version='v3') class TestListableApiSubResource(unittest.TestCase): @@ -103,7 +132,7 @@ def test_all(self): result = OrderCoupons.all(1, connection, limit=2) self.assertEqual(len(result), 2) - connection.make_request.assert_called_once_with('GET', 'orders/1/coupons', None, {'limit': 2}, None) + connection.make_request.assert_called_once_with('GET', 'orders/1/coupons', None, {'limit': 2}, None, version='v2') def test_google_mappings(self): connection = MagicMock() @@ -111,8 +140,7 @@ def test_google_mappings(self): result = GoogleProductSearchMappings.all(1, connection, limit=2) self.assertEqual(len(result), 2) - connection.make_request.assert_called_once_with('GET', 'products/1/googleproductsearch', None, {'limit': 2}, None) - + connection.make_request.assert_called_once_with('GET', 'products/1/googleproductsearch', None, {'limit': 2}, None, version='v2') class TestUpdateableApiResource(unittest.TestCase): def test_update(self): @@ -123,7 +151,7 @@ def test_update(self): new_order = order.update(name='order') self.assertIsInstance(new_order, Orders) - connection.make_request.assert_called_once_with('PUT', 'orders/1', {'name': 'order'}, None, None) + connection.make_request.assert_called_once_with('PUT', 'orders/1', {'name': 'order'}, None, None, version='v2') class TestUpdateableApiSubResource(unittest.TestCase): @@ -136,7 +164,7 @@ def test_update(self): self.assertIsInstance(new_order, OrderShipments) connection.make_request.assert_called_once_with('PUT', 'orders/2/shipments/1', {'tracking_number': '1234'}, - None, None) + None, None, version='v2') class TestDeleteableApiResource(unittest.TestCase): @@ -146,7 +174,7 @@ def test_delete_all(self): self.assertEqual(Orders.delete_all(connection), {}) - connection.make_request.assert_called_once_with('DELETE', 'orders', None, None, None) + connection.make_request.assert_called_once_with('DELETE', 'orders', None, None, None, version='v2') def test_delete(self): connection = MagicMock() @@ -156,7 +184,7 @@ def test_delete(self): self.assertEqual(order.delete(), {}) - connection.make_request.assert_called_once_with('DELETE', 'orders/1', None, None, None) + connection.make_request.assert_called_once_with('DELETE', 'orders/1', None, None, None, version='v2') class TestDeleteableApiSubResource(unittest.TestCase): @@ -166,7 +194,7 @@ def test_delete_all(self): self.assertEqual(OrderShipments.delete_all(1, connection=connection), {}) - connection.make_request.assert_called_once_with('DELETE', 'orders/1/shipments', None, None, None) + connection.make_request.assert_called_once_with('DELETE', 'orders/1/shipments', None, None, None, version='v2') def test_delete(self): connection = MagicMock() @@ -175,7 +203,7 @@ def test_delete(self): shipment = OrderShipments({'id': 1, 'order_id': 2, '_connection': connection}) self.assertEqual(shipment.delete(), {}) - connection.make_request.assert_called_once_with('DELETE', 'orders/2/shipments/1', None, None, None) + connection.make_request.assert_called_once_with('DELETE', 'orders/2/shipments/1', None, None, None, version='v2') class TestCountableApiResource(unittest.TestCase): @@ -184,7 +212,7 @@ def test_count(self): connection.make_request.return_value = {'count': 2} self.assertEqual(Products.count(connection, is_visible=True), 2) - connection.make_request.assert_called_once_with('GET', 'products/count', None, {'is_visible': True}, None) + connection.make_request.assert_called_once_with('GET', 'products/count', None, {'is_visible': True}, None, version='v2') class TestCountableApiSubResource(unittest.TestCase): @@ -194,7 +222,7 @@ def test_count(self): self.assertEqual(CountryStates.count(1, connection=connection, is_visible=True), 2) connection.make_request.assert_called_once_with('GET', 'countries/1/states/count', - None, {'is_visible': True}, None) + None, {'is_visible': True}, None, version='v2') def test_count_with_custom_count_path(self): connection = MagicMock() @@ -202,4 +230,4 @@ def test_count_with_custom_count_path(self): self.assertEqual(OrderShipments.count(connection=connection, is_visible=True), 2) connection.make_request.assert_called_once_with('GET', 'orders/shipments/count', - None, {'is_visible': True}, None) + None, {'is_visible': True}, None, version='v2')