diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__init__.pyc b/__init__.pyc new file mode 100644 index 0000000..65b7979 Binary files /dev/null and b/__init__.pyc differ diff --git a/magento2/__init__.pyc b/magento2/__init__.pyc index ac28b82..ed53620 100644 Binary files a/magento2/__init__.pyc and b/magento2/__init__.pyc differ diff --git a/magento2/api.py b/magento2/api.py index 02f3222..1340f69 100644 --- a/magento2/api.py +++ b/magento2/api.py @@ -201,7 +201,7 @@ def __exit__(self, type, value, traceback): self.client.service.endSession(self.session) self.session = None - def get(self, resource_path, arguments): + def get(self, resource_path, arguments={}): """ Proxy for REST call API """ @@ -209,7 +209,7 @@ def get(self, resource_path, arguments): raise NotImplementedError("Currently only supports REST API") return self.client.get(resource_path, arguments) - def post(self, resource_path, arguments): + def post(self, resource_path, arguments={}): """ Proxy for REST call API """ @@ -217,7 +217,7 @@ def post(self, resource_path, arguments): raise NotImplementedError("Currently only supports REST API") return self.client.post(resource_path, arguments) - def put(self, resource_path, arguments): + def put(self, resource_path, arguments={}): """ Proxy for REST call API """ @@ -225,13 +225,13 @@ def put(self, resource_path, arguments): raise NotImplementedError("Currently only supports REST API") return self.client.put(resource_path, arguments) - def delete(self, resource_path, arguments): + def delete(self, resource_path, arguments={}): """ Proxy for REST call API """ if self.protocol != 'rest': raise NotImplementedError("Currently only supports REST API") - return self.client.delete(resource_path, arguments) + return self.client.delete(resource_path, arguments={}) def patch(self, resource_path, arguments): """ @@ -239,7 +239,7 @@ def patch(self, resource_path, arguments): """ if self.protocol != 'rest': raise NotImplementedError("Currently only supports REST API") - return self.client.patch(resource_path, arguments) + return self.client.patch(resource_path, arguments={}) def multiCall(self, calls): """ diff --git a/magento2/api.pyc b/magento2/api.pyc index c000b18..7715673 100644 Binary files a/magento2/api.pyc and b/magento2/api.pyc differ diff --git a/magento2/catalog.py b/magento2/catalog.py index 38150ab..2793d1f 100644 --- a/magento2/catalog.py +++ b/magento2/catalog.py @@ -10,7 +10,7 @@ import warnings from .api import API - +import logging class Category(API): """ @@ -25,10 +25,10 @@ def currentStore(self, store_view=None): :param store_view: Store view ID or Code :return: int """ - args = [store_view] if store_view else [] + args = [store_view] if store_view else {} return int(self.call('catalog_category.currentStore', args)) - def tree(self, parent_id=None, store_view=None): + def tree(self, rootCatId=2, store_view=1): """ Retrieve hierarchical tree of categories. @@ -36,7 +36,7 @@ def tree(self, parent_id=None, store_view=None): :param store_view: Store View (optional) :return: dictionary of values """ - return self.call('catalog_category.tree', [parent_id, store_view]) + return self.get('categories?rootCategoryId=%s' % (rootCatId,), {}) def level(self, website=None, store_view=None, parent_category=None): """ @@ -51,6 +51,19 @@ def level(self, website=None, store_view=None, parent_category=None): 'catalog_category.level', [website, store_view, parent_category] ) + def info_by_name(self, category_name, store_view=None,): + """ + Retrieve Category details by name + + :param category_name: ID of category to retrieve + :param store_view: Store view ID or code + :return: Dictionary of data + """ + search = "?searchCriteria[filter_groups][0][filters][0][field]=category_name&searchCriteria[filter_groups][0][filters][0][value]=%s" \ + "&searchCriteria[filter_groups][0][filters][0][condition_type]=eq" % (category_name,) + + return self.get('categories/list'+search, {}) + def info(self, category_id, store_view=None, attributes=None): """ Retrieve Category details @@ -67,7 +80,7 @@ def info(self, category_id, store_view=None, attributes=None): } ) - def create(self, parent_id, data, store_view=None): + def create(self, parent_id, name, level=4): """ Create new category and return its ID @@ -76,14 +89,17 @@ def create(self, parent_id, data, store_view=None): :param store_view: Store view ID or Code :return: Integer ID """ - return int(self.post( - 'categories', - { - 'parent_id': parent_id, - 'data': data, - 'storeId': store_view + + data = { + 'category' : { + 'parent_id': parent_id, + 'level': level, + 'name' : name, + 'is_active': True + } } - )) + + return self.post('categories', data) def update(self, category_id, data, store_view=None): """ @@ -232,15 +248,6 @@ class Product(API): """ __slots__ = () - def currentStore(self, store_view=None): - """ - Set/Get current store view - - :param store_view: Store view ID or Code - :return: int - """ - args = [store_view] if store_view else [] - return int(self.call('catalog_product.currentStore', args)) def list(self, filters=None, store_view=None): """ @@ -277,13 +284,10 @@ def info(self, product, store_view=None): :return: `dict` of values """ return self.get( - 'products/%s' % (product,), - { - 'storeId': store_view - } + 'products/%s' % (product,) ) - def create(self, product_type, attribute_set_id, sku, data): + def create(self, type_id, attribute_set_id, sku, data): """ Create Product and return ID @@ -293,17 +297,22 @@ def create(self, product_type, attribute_set_id, sku, data): :param data: Dictionary of data :return: INT id of product created """ - return int(self.post( - 'products', - { - 'data': data, - 'product_type': product_type, - 'attribute_set_id': attribute_set_id, - 'sku': sku, - }) + product = { + 'product': { + 'type_id': type_id, + 'attribute_set_id': attribute_set_id, + 'sku': sku, + } + } + + product['product'].update(data) + + return self.post( + 'products',product + ) - def update(self, product, data, store_view=None, identifierType=None): + def update(self, sku, data): """ Update product Information @@ -315,52 +324,15 @@ def update(self, product, data, store_view=None, identifierType=None): :return: Boolean """ - return bool(self.call( - 'catalog_product.update', - [product, data, store_view, identifierType] - )) - - def setSpecialPrice(self, product, special_price=None, - from_date=None, to_date=None, store_view=None, - identifierType=None): - """ - Update product's special price - - :param product: ID or SKU of product - :param special_price: Special Price - :param from_date: From date - :param to_date: To Date - :param store_view: ID or Code of Store View - :param identifierType: Defines whether the product or SKU value is - passed in the "product" parameter. - - :return: Boolean - """ - return bool(self.call( - 'catalog_product.setSpecialPrice', [ - product, special_price, from_date, to_date, store_view, - identifierType - ] - )) - - def getSpecialPrice(self, product, store_view=None, identifierType=None): - """ - Get product special price data - - :param product: ID or SKU of product - :param store_view: ID or Code of Store view - :param identifierType: Defines whether the product or SKU value is - passed in the "product" parameter. - - :return: Dictionary - """ - return self.call( - 'catalog_product.getSpecialPrice', [ - product, store_view, identifierType - ] + return self.put( + 'products/%s' % (sku,), + { + 'product': { + }.update(data) + } ) - def delete(self, product, identifierType=None): + def remove(self, sku): """ Delete a product @@ -370,10 +342,9 @@ def delete(self, product, identifierType=None): :return: Boolean """ - return bool(self.call('catalog_product.delete', [ - product, identifierType - ])) - + return self.delete( + 'products/%s' % (sku,), {} + ) class ProductAttribute(API): """ @@ -381,16 +352,6 @@ class ProductAttribute(API): """ __slots__ = () - def currentStore(self, store_view=None): - """ - Set/Get current store view - - :param store_view: Store view ID or Code - :return: int - """ - args = [store_view] if store_view else [] - return int(self.call('catalog_product_attribute.currentStore', args)) - def list(self, attribute_set_id): """ Retrieve product attribute list @@ -398,76 +359,135 @@ def list(self, attribute_set_id): :param attribute_set_id: ID of attribute set :return: `list` of `dict` """ - return self.call('catalog_product_attribute.list', [attribute_set_id]) + return self.get('products/attribute-sets/%s/attributes' % (attribute_set_id,), {}) - def info(self, attribute): + def info(self, attributeCode): """ Retrieve product attribute info :param attribute: ID or Code of the attribute :return: `list` of `dict` """ - return self.call('catalog_product_attribute.info', [attribute]) + return self.get('products/attributes/%s' % (attributeCode,), {}) - def options(self, attribute, store_view=None): + def options(self, attribute): """ Retrieve product attribute options - :param attribute: ID or Code of the attribute + :param attribute: Code of the attribute :return: `list` of `dict` """ - return self.call('catalog_product_attribute.options', - [attribute, store_view]) + return self.get('products/attributes/%s/options' % (attribute,), {}) - def addOption(self, attribute, data): + def addOption(self, attribute, label, value, data): """ Create new options to attribute (Magento > 1.7.0) - :param attribute: ID or Code of the attribute. + :param attribute: Code of the attribute. :param data: Dictionary of Data. - {'label':[{'store_id':[0,1], 'value':'Value'},], 'order':1, 'is_default':1} :return: True if created. """ - return bool(self.call('product_attribute.addOption', - [attribute, data])) + return self.post('products/attributes/%s/options' % (attribute,), { + 'option': { + 'label': label, + 'value': value, + }.update(data) + }) - def createOption(self, *a, **kw): - warnings.warn( - "ProductAttribute: createOption is deprecated, use addOption instead." - ) - return self.addOption(*a, **kw) - - def removeOption(self, attribute, option): + def removeOption(self, attributeCode, optionId): """ Remove option to attribute (Magento > 1.7.0) - :param attribute: ID or Code of the attribute. - :param option: Option ID. + :param attributeCode: Code of the attribute. + :param optionId: Option ID. :return: True if the option is removed. """ - return bool(self.call('product_attribute.removeOption', - [attribute, option])) + return self.delete('products/attributes/%s/options/%s' % (attributeCode, optionId), {}) - def create(self, data): + def create(self, attribute_code, label, frontendinput, data): """ Create attribute entity. :param data: Dictionary of entity data to create attribute with. + :param attribute_code: Code for new attribute. + :param label: Label for new attribute. + :param frontendinput: Frontend Input type for new attribute use getTypes for a list of available. :return: Integer ID of attribute created """ - return self.call('catalog_product_attribute.create', [data]) + return self.post('products/attributes', + { + 'attribute': { + 'attribute_code': attribute_code, + 'default_frontend_label': label, + 'frontend_input': frontendinput + }.update(data) + }) - def update(self, attribute, data): + def update(self, attributeCode, data): """ Update attribute entity data. - :param attribute: ID or Code of the attribute. + :param attribute: Code of the attribute. :param data: Dictionary of entity data to update on attribute. - :return: Boolean + :return: Dictionary """ - return self.call('catalog_product_attribute.update', [attribute, data]) + return self.post('products/attributes/%s' % (attributeCode,), + { + 'attribute': { + }.update(data) + }) + + def getTypes(self): + """ + Get attribute frontend types. + :return: Dictionary + """ + return self.get('products/attributes/types', {}) + + +class ProductAttributeGroup(API): + """ + Product Attribute Group API + """ + __slots__ = () + + def list(self, searchString=''): + """ + Retrieve list of product attribute sets + + :return: `list` of `dict` + """ + return self.get('products/attribute-sets/sets/list', []) + + def create(self, attribute_group_name, attribute_set_id, attributes={}): + """ + Create a new attribute set based on a "skeleton" attribute set. + If unsure, use the "Default" attribute set as a skeleton. + + :param attribute_set_name: name of the new attribute set + :param skeleton_set_id: id of the skeleton attribute set to base this set on. + :param attributes: dictionary of extension attributes. + + :return: Integer ID of new attribute set + """ + data = { + "group": { + "attribute_group_name": attribute_group_name, + "attribute_set_id": attribute_set_id, + "extension_attributes": attributes + } + } + return self.post('products/attribute-sets/groups', data) + + def remove(self, groupdId): + """ + Retrieve list of product attribute sets + + :return: `list` of `dict` + """ + return self.delete('products/attribute-sets/groups/%s' % (groupdId,), {}) class ProductAttributeSet(API): @@ -476,27 +496,39 @@ class ProductAttributeSet(API): """ __slots__ = () - def list(self): + def list(self, searchString=''): """ Retrieve list of product attribute sets :return: `list` of `dict` """ - return self.call('catalog_product_attribute_set.list', []) + return self.get('products/attribute-sets/sets/list', []) - def create(self, attribute_set_name, skeleton_set_id): + def create(self, attribute_set_name, skeleton_set_id, attributes={}): """ Create a new attribute set based on a "skeleton" attribute set. If unsure, use the "Default" attribute set as a skeleton. :param attribute_set_name: name of the new attribute set :param skeleton_set_id: id of the skeleton attribute set to base this set on. + :param attributes: dictionary of extension attributes. :return: Integer ID of new attribute set """ - return self.call('catalog_product_attribute_set.create', [attribute_set_name, skeleton_set_id]) - def attributeAdd(self, attribute_id, attribute_set_id): + data = { + + "attributeSet": { + "attribute_set_name": attribute_set_name, + "sort_order": 0, + "entity_type_id": 0, + "extension_attributes": attributes + }, + "skeletonId": skeleton_set_id + } + return self.post('products/attribute-sets', data) + + def attributeAdd(self, attribute_set_id, group_id, code): """ Add an existing attribute to an attribute set. @@ -505,9 +537,15 @@ def attributeAdd(self, attribute_id, attribute_set_id): :return: Boolean """ - return self.call('catalog_product_attribute_set.attributeAdd', [attribute_id, attribute_set_id]) - def attributeRemove(self, attribute_id, attribute_set_id): + data = { + "attributeSetId": attribute_set_id, + "attributeGroupId": group_id, + "attributeCode": code, + } + return self.post('products/attribute-sets/attributes', data) + + def attributeRemove(self, atribute_code, attribute_set_id): """ Remove an existing attribute to an attribute set. @@ -516,7 +554,7 @@ def attributeRemove(self, attribute_id, attribute_set_id): :return: Boolean """ - return self.call('catalog_product_attribute_set.attributeRemove', [attribute_id, attribute_set_id]) + return self.delete('products/attribute-sets/%s/attributes/%s' % (attribute_set_id, atribute_code,), {}) @@ -532,7 +570,7 @@ def list(self): :return: `list` of `dict` """ - return self.call('catalog_product_type.list', []) + return self.get('products/types', {}) class ProductImages(API): @@ -870,7 +908,7 @@ def list(self, products): :param products: list of IDs or SKUs of products :return: `list` of `dict` """ - return self.call('cataloginventory_stock_item.list', [products]) + return self.get('stockItems') def update(self, product, data): """ diff --git a/magento2/catalog.pyc b/magento2/catalog.pyc index 7734898..adff8c2 100644 Binary files a/magento2/catalog.pyc and b/magento2/catalog.pyc differ diff --git a/magento2/checkout.pyc b/magento2/checkout.pyc index ae35349..428b4c6 100644 Binary files a/magento2/checkout.pyc and b/magento2/checkout.pyc differ diff --git a/magento2/customer.pyc b/magento2/customer.pyc index 76b083e..00842e6 100644 Binary files a/magento2/customer.pyc and b/magento2/customer.pyc differ diff --git a/magento2/directory.pyc b/magento2/directory.pyc index da2eb97..14e770a 100644 Binary files a/magento2/directory.pyc and b/magento2/directory.pyc differ diff --git a/magento2/miscellaneous.pyc b/magento2/miscellaneous.pyc index 1a07720..e8928e6 100644 Binary files a/magento2/miscellaneous.pyc and b/magento2/miscellaneous.pyc differ diff --git a/magento2/rest.py b/magento2/rest.py index 4784173..73a7158 100644 --- a/magento2/rest.py +++ b/magento2/rest.py @@ -2,13 +2,14 @@ try: import requests import json + import logging except ImportError: pass class Client(object): - def __init__(self, url, token, verify_ssl=True): + def __init__(self, url, token, verify_ssl=False): self._url = url self._token = token self._verify_ssl = verify_ssl @@ -31,6 +32,7 @@ def login(self, username, password): # TODO: Add Error Handling here if not status=200 OK ! token = requests.post( url, data=payload, verify=self._verify_ssl, headers=headers) + self._token = json.loads(token.text) def get(self, resource_path, arguments): @@ -38,7 +40,6 @@ def get(self, resource_path, arguments): res = requests.get( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() def post(self, resource_path, arguments): @@ -46,7 +47,6 @@ def post(self, resource_path, arguments): res = requests.post( url, data=json.dumps(arguments), verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token, "Content-Type": "application/json"}) - res.raise_for_status() return res.json() def put(self, resource_path, arguments): @@ -54,7 +54,6 @@ def put(self, resource_path, arguments): res = requests.put( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() def delete(self, resource_path, arguments): @@ -62,7 +61,6 @@ def delete(self, resource_path, arguments): res = requests.delete( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() def patch(self, resource_path, arguments): @@ -70,5 +68,4 @@ def patch(self, resource_path, arguments): res = requests.patch( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() diff --git a/magento2/rest.pyc b/magento2/rest.pyc index 5c2227b..5eb2c1b 100644 Binary files a/magento2/rest.pyc and b/magento2/rest.pyc differ diff --git a/magento2/sales.pyc b/magento2/sales.pyc index 40114a3..e21a617 100644 Binary files a/magento2/sales.pyc and b/magento2/sales.pyc differ diff --git a/magento2/utils.pyc b/magento2/utils.pyc deleted file mode 100644 index 2d2205b..0000000 Binary files a/magento2/utils.pyc and /dev/null differ diff --git a/magento2/version.pyc b/magento2/version.pyc index f8de333..fcfd78e 100644 Binary files a/magento2/version.pyc and b/magento2/version.pyc differ diff --git a/test/test.py b/test.py similarity index 96% rename from test/test.py rename to test.py index 7cdd475..18547b4 100644 --- a/test/test.py +++ b/test.py @@ -1,4 +1,6 @@ -import magento2 +import sys +from magento2dev import magento2 + import logging _logger = logging.getLogger(__name__)