From 4c26c2410849125b14230ba584b563f4bee1d259 Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 11:16:25 +0000 Subject: [PATCH 1/7] feat: allow the specification of the Shopify API version --- README.md | 1 + src/shopify/base.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 6d234dc..d77c74a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ API client for the [Shopify](https://www.shopify.com) service. | Name | Type | Default | Description | | ------------------------ | ----- | ------- | ---------------------------------------------------------------------------------------------------------- | +| **SHOPIFY_API_VERSION** | `str` | `None` | The Shopify API version to be used. Example: `2023-01`. | | **SHOPIFY_API_KEY** | `str` | `None` | The username/key to be used in the authentication with the Shopify API. | | **SHOPIFY_PASSWORD** | `str` | `None` | The password to be used in the authentication with the Shopify API. | | **SHOPIFY_SECRET** | `str` | `None` | The shared secret to be used for message validation with the Shopify API. | diff --git a/src/shopify/base.py b/src/shopify/base.py index a51178d..82d4d93 100644 --- a/src/shopify/base.py +++ b/src/shopify/base.py @@ -85,11 +85,13 @@ class API( def __init__(self, *args, **kwargs): appier.API.__init__(self, *args, **kwargs) + self.api_version = appier.conf("SHOPIFY_API_VERSION", None) self.api_key = appier.conf("SHOPIFY_API_KEY", None) self.password = appier.conf("SHOPIFY_PASSWORD", None) self.secret = appier.conf("SHOPIFY_SECRET", None) self.store_url = appier.conf("SHOPIFY_STORE", None) self.website_url = appier.conf("SHOPIFY_WEBSITE", None) + self.api_version = kwargs.get("api_version", self.api_version) self.api_key = kwargs.get("api_key", self.api_key) self.password = kwargs.get("password", self.password) self.secret = kwargs.get("secret", self.secret) @@ -198,6 +200,7 @@ def _build_url(self): self.api_key, self.password, self.store_url ) self.website_url = "http://%s/" % (self.website_url or self.store_url) + self.api_version_path = "api/%s" % (self.api_version or "") #################### 2023-01 class OAuthAPI(appier.OAuth2API, API): From f098972900797b10af9ea3e036b39d57dfd49f39 Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 11:16:36 +0000 Subject: [PATCH 2/7] feat: update product module --- src/shopify/product.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/shopify/product.py b/src/shopify/product.py index d35a813..2965f99 100644 --- a/src/shopify/product.py +++ b/src/shopify/product.py @@ -40,12 +40,12 @@ class ProductAPI(object): def list_products(self, *args, **kwargs): - url = self.base_url + "admin/products.json" + url = self.base_url + "admin/%s/products.json" % self.api_version_path contents = self.get(url, **kwargs) return contents["products"] def many_products(self, *args, **kwargs): - url = self.base_url + "admin/products.json" + url = self.base_url + "admin/%s/products.json" % self.api_version_path contents = self.get_many( url, key = "products", @@ -54,12 +54,12 @@ def many_products(self, *args, **kwargs): return contents["products"] def count_products(self, *args, **kwargs): - url = self.base_url + "admin/products/count.json" + url = self.base_url + "admin/%s/products/count.json" % self.api_version_path contents = self.get(url, **kwargs) return contents["count"] def create_product(self, product): - url = self.base_url + "admin/products.json" + url = self.base_url + "admin/%s/products.json" % self.api_version_path contents = self.post( url, data_j = dict(product = product) @@ -67,36 +67,36 @@ def create_product(self, product): return contents["product"] def get_product(self, id): - url = self.base_url + "admin/products/%d.json" % id + url = self.base_url + "admin/%s/products/%d.json" % (self.api_version_path, id) contents = self.get(url) return contents["product"] def delete_product(self, id): - url = self.base_url + "admin/products/%d.json" % id + url = self.base_url + "admin/%s/products/%d.json" % (self.api_version_path, id) self.delete(url) def images_product(self, id, *args, **kwargs): - url = self.base_url + "admin/products/%d/images.json" % id + url = self.base_url + "admin/%s/products/%d/images.json" % (self.api_version_path, id) contents = self.get(url, **kwargs) return contents["images"] def create_image_product(self, id, image): - url = self.base_url + "admin/products/%d/images.json" % id + url = self.base_url + "admin/%s/products/%d/images.json" % (self.api_version_path, id) contents = self.post(url, data_j = dict(image = image)) return contents["image"] def delete_image_product(self, id, image_id): - url = self.base_url + "admin/products/%d/images/%d.json" % (id, image_id) + url = self.base_url + "admin/%s/products/%d/images/%d.json" % (self.api_version_path, id, image_id) self.delete(url) def metafields_product(self, id, *args, **kwargs): - url = self.base_url + "admin/products/%d/metafields.json" % id + url = self.base_url + "admin/%s/products/%d/metafields.json" % (self.api_version_path, id) contents = self.get(url, **kwargs) return contents["metafields"] def create_metafield_product(self, id, key, value, type = None, value_type = None, namespace = "global"): type = type or value_type or "string" - url = self.base_url + "admin/products/%d/metafields.json" % id + url = self.base_url + "admin/%s/products/%d/metafields.json" % (self.api_version_path, id) contents = self.post( url, data_j = dict( From 4b2bb122937f70d9e979c6d1ec0d13fcad207e0d Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 11:31:13 +0000 Subject: [PATCH 3/7] feat: simplify by creating `admin_url` --- src/shopify/base.py | 4 +++- src/shopify/product.py | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/shopify/base.py b/src/shopify/base.py index 82d4d93..f159b2b 100644 --- a/src/shopify/base.py +++ b/src/shopify/base.py @@ -200,7 +200,9 @@ def _build_url(self): self.api_key, self.password, self.store_url ) self.website_url = "http://%s/" % (self.website_url or self.store_url) - self.api_version_path = "api/%s" % (self.api_version or "") #################### 2023-01 + + api_version_path = "api/%s/" % self.api_version if self.api_version else "" + self.admin_url = "%sadmin/%s" % (self.base_url, api_version_path) class OAuthAPI(appier.OAuth2API, API): diff --git a/src/shopify/product.py b/src/shopify/product.py index 2965f99..d037be7 100644 --- a/src/shopify/product.py +++ b/src/shopify/product.py @@ -40,12 +40,12 @@ class ProductAPI(object): def list_products(self, *args, **kwargs): - url = self.base_url + "admin/%s/products.json" % self.api_version_path + url = self.admin_url + "products.json" contents = self.get(url, **kwargs) return contents["products"] def many_products(self, *args, **kwargs): - url = self.base_url + "admin/%s/products.json" % self.api_version_path + url = self.admin_url + "products.json" contents = self.get_many( url, key = "products", @@ -54,12 +54,12 @@ def many_products(self, *args, **kwargs): return contents["products"] def count_products(self, *args, **kwargs): - url = self.base_url + "admin/%s/products/count.json" % self.api_version_path + url = self.admin_url + "products/count.json" contents = self.get(url, **kwargs) return contents["count"] def create_product(self, product): - url = self.base_url + "admin/%s/products.json" % self.api_version_path + url = self.admin_url + "products.json" contents = self.post( url, data_j = dict(product = product) @@ -67,36 +67,36 @@ def create_product(self, product): return contents["product"] def get_product(self, id): - url = self.base_url + "admin/%s/products/%d.json" % (self.api_version_path, id) + url = self.admin_url + "products/%d.json" % id contents = self.get(url) return contents["product"] def delete_product(self, id): - url = self.base_url + "admin/%s/products/%d.json" % (self.api_version_path, id) + url = self.admin_url + "products/%d.json" % id self.delete(url) def images_product(self, id, *args, **kwargs): - url = self.base_url + "admin/%s/products/%d/images.json" % (self.api_version_path, id) + url = self.admin_url + "products/%d/images.json" % id contents = self.get(url, **kwargs) return contents["images"] def create_image_product(self, id, image): - url = self.base_url + "admin/%s/products/%d/images.json" % (self.api_version_path, id) + url = self.admin_url + "products/%d/images.json" % id contents = self.post(url, data_j = dict(image = image)) return contents["image"] def delete_image_product(self, id, image_id): - url = self.base_url + "admin/%s/products/%d/images/%d.json" % (self.api_version_path, id, image_id) + url = self.admin_url + "products/%d/images/%d.json" % (id, image_id) self.delete(url) def metafields_product(self, id, *args, **kwargs): - url = self.base_url + "admin/%s/products/%d/metafields.json" % (self.api_version_path, id) + url = self.admin_url + "products/%d/metafields.json" % id contents = self.get(url, **kwargs) return contents["metafields"] def create_metafield_product(self, id, key, value, type = None, value_type = None, namespace = "global"): type = type or value_type or "string" - url = self.base_url + "admin/%s/products/%d/metafields.json" % (self.api_version_path, id) + url = self.admin_url + "products/%d/metafields.json" % id contents = self.post( url, data_j = dict( From f117553c549c282b93df6b474b6b8454dafd1dac Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 11:39:55 +0000 Subject: [PATCH 4/7] feat: use `admin_url` for admin endpoints --- src/shopify/inventory_item.py | 6 +++--- src/shopify/location.py | 2 +- src/shopify/order.py | 22 +++++++++++----------- src/shopify/shop.py | 2 +- src/shopify/smart_collection.py | 12 ++++++------ src/shopify/webhook.py | 10 +++++----- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/shopify/inventory_item.py b/src/shopify/inventory_item.py index 2a597b7..93a9e01 100644 --- a/src/shopify/inventory_item.py +++ b/src/shopify/inventory_item.py @@ -40,16 +40,16 @@ class InventoryItemAPI(object): def list_inventory_items(self, *args, **kwargs): - url = self.base_url + "admin/inventory_items.json" + url = self.admin_url + "inventory_items.json" contents = self.get(url, **kwargs) return contents["inventory_items"] def get_inventory_item(self, id): - url = self.base_url + "admin/inventory_items/%d.json" % id + url = self.admin_url + "inventory_items/%d.json" % id contents = self.get(url) return contents["inventory_item"] def update_inventory_item(self, id, inventory_item): - url = self.base_url + "admin/inventory_items/%d.json" % id + url = self.admin_url + "inventory_items/%d.json" % id contents = self.put(url, data_j = dict(inventory_item = inventory_item)) return contents["inventory_item"] diff --git a/src/shopify/location.py b/src/shopify/location.py index 2d682ae..54de9d7 100644 --- a/src/shopify/location.py +++ b/src/shopify/location.py @@ -40,6 +40,6 @@ class LocationAPI(object): def list_locations(self, *args, **kwargs): - url = self.base_url + "admin/locations.json" + url = self.admin_url + "locations.json" contents = self.get(url, **kwargs) return contents["locations"] diff --git a/src/shopify/order.py b/src/shopify/order.py index 0352fee..a69aa2f 100644 --- a/src/shopify/order.py +++ b/src/shopify/order.py @@ -40,12 +40,12 @@ class OrderAPI(object): def list_orders(self, *args, **kwargs): - url = self.base_url + "admin/orders.json" + url = self.admin_url + "orders.json" contents = self.get(url, **kwargs) return contents["orders"] def list_orders_a(self, limit = 50, all = True, **kwargs): - url = self.base_url + "admin/orders.json" + url = self.admin_url + "orders.json" orders = self._fetch_many( url, item_name = "orders", @@ -57,31 +57,31 @@ def list_orders_a(self, limit = 50, all = True, **kwargs): return orders def get_order(self, id): - url = self.base_url + "admin/orders/%d.json" % id + url = self.admin_url + "orders/%d.json" % id contents = self.get(url) return contents["order"] def count_orders(self, *args, **kwargs): - url = self.base_url + "admin/orders/count.json" + url = self.admin_url + "orders/count.json" contents = self.get(url, **kwargs) return contents["count"] def transactions_order(self, id): - url = self.base_url + "admin/orders/%d/transactions.json" % id + url = self.admin_url + "orders/%d/transactions.json" % id contents = self.get(url) return contents["transactions"] def update_order(self, id, **kwargs): order = dict(kwargs) order["id"] = str(id) - url = self.base_url + "admin/orders/%d.json" % id + url = self.admin_url + "orders/%d.json" % id self.put( url, data_j = dict(order = order) ) def pay_order(self, id): - url = self.base_url + "admin/orders/%d/transactions.json" % id + url = self.admin_url + "orders/%d/transactions.json" % id self.post( url, data_j = dict( @@ -92,7 +92,7 @@ def pay_order(self, id): ) def cancel_order(self, id, restock = True, email = False): - url = self.base_url + "admin/orders/%d/cancel.json" % id + url = self.admin_url + "orders/%d/cancel.json" % id self.post( url, data_j = dict( @@ -104,20 +104,20 @@ def cancel_order(self, id, restock = True, email = False): def fulfill_order(self, id, location_id, **kwargs): fulfillment = dict(kwargs) fulfillment["location_id"] = location_id - url = self.base_url + "admin/orders/%d/fulfillments.json" % id + url = self.admin_url + "orders/%d/fulfillments.json" % id self.post( url, data_j = dict(fulfillment = fulfillment) ) def metafields_order(self, id, *args, **kwargs): - url = self.base_url + "admin/orders/%d/metafields.json" % id + url = self.admin_url + "orders/%d/metafields.json" % id contents = self.get(url, **kwargs) return contents["metafields"] def create_metafield_order(self, id, key, value, type = None, value_type = None, namespace = "global"): type = type or value_type or "string" - url = self.base_url + "admin/orders/%d/metafields.json" % id + url = self.admin_url + "orders/%d/metafields.json" % id contents = self.post( url, data_j = dict( diff --git a/src/shopify/shop.py b/src/shopify/shop.py index 5334205..a51603e 100644 --- a/src/shopify/shop.py +++ b/src/shopify/shop.py @@ -40,6 +40,6 @@ class ShopAPI(object): def get_shop(self, *args, **kwargs): - url = self.base_url + "admin/shop.json" + url = self.admin_url + "shop.json" contents = self.get(url, **kwargs) return contents["shop"] diff --git a/src/shopify/smart_collection.py b/src/shopify/smart_collection.py index 96f3224..8c5409c 100644 --- a/src/shopify/smart_collection.py +++ b/src/shopify/smart_collection.py @@ -40,12 +40,12 @@ class SmartCollectionAPI(object): def list_smart_collections(self, *args, **kwargs): - url = self.base_url + "admin/smart_collections.json" + url = self.admin_url + "smart_collections.json" contents = self.get(url, **kwargs) return contents["smart_collections"] def many_smart_collections(self, *args, **kwargs): - url = self.base_url + "admin/smart_collections.json" + url = self.admin_url + "smart_collections.json" contents = self.get_many( url, key = "smart_collections", @@ -54,20 +54,20 @@ def many_smart_collections(self, *args, **kwargs): return contents["smart_collections"] def create_smart_collection(self, smart_collection): - url = self.base_url + "admin/smart_collections.json" + url = self.admin_url + "smart_collections.json" contents = self.post(url, data_j = dict(smart_collection = smart_collection)) return contents["smart_collection"] def get_smart_collection(self, id): - url = self.base_url + "admin/smart_collections/%d.json" % id + url = self.admin_url + "smart_collections/%d.json" % id contents = self.get(url) return contents["smart_collection"] def update_smart_collection(self, id, smart_collection): - url = self.base_url + "admin/smart_collections/%d.json" % id + url = self.admin_url + "smart_collections/%d.json" % id contents = self.put(url, data_j = dict(smart_collection = smart_collection)) return contents["smart_collection"] def delete_smart_collection(self, id): - url = self.base_url + "admin/smart_collections/%d.json" % id + url = self.admin_url + "smart_collections/%d.json" % id self.delete(url) diff --git a/src/shopify/webhook.py b/src/shopify/webhook.py index 2029d21..2335f2c 100644 --- a/src/shopify/webhook.py +++ b/src/shopify/webhook.py @@ -40,25 +40,25 @@ class WebhookAPI(object): def list_webhooks(self, *args, **kwargs): - url = self.base_url + "admin/webhooks.json" + url = self.admin_url + "webhooks.json" contents = self.get(url, **kwargs) return contents["webhooks"] def create_webhook(self, webhook): - url = self.base_url + "admin/webhooks.json" + url = self.admin_url + "webhooks.json" contents = self.post(url, data_j = dict(webhook = webhook)) return contents["webhook"] def get_webhook(self, id): - url = self.base_url + "admin/webhooks/%d.json" % id + url = self.admin_url + "webhooks/%d.json" % id contents = self.get(url) return contents["webhook"] def update_webhook(self, id, webhook): - url = self.base_url + "admin/webhooks/%d.json" % id + url = self.admin_url + "webhooks/%d.json" % id contents = self.put(url, data_j = dict(webhook = webhook)) return contents["webhook"] def delete_webhook(self, id): - url = self.base_url + "admin/webhooks/%d.json" % id + url = self.admin_url + "webhooks/%d.json" % id self.delete(url) From cdb1d8c0b1cdf22ab2aae91c16c75f256f04b22d Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 11:41:14 +0000 Subject: [PATCH 5/7] feat: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d96d1c..efa02e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -* +* Add support for versioned admin endpoints ### Changed From 724e5e7e0f0ec1002ce44940faf55008664061be Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 11:45:50 +0000 Subject: [PATCH 6/7] refactor: simplify --- src/shopify/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shopify/base.py b/src/shopify/base.py index f159b2b..d9e0b89 100644 --- a/src/shopify/base.py +++ b/src/shopify/base.py @@ -202,7 +202,7 @@ def _build_url(self): self.website_url = "http://%s/" % (self.website_url or self.store_url) api_version_path = "api/%s/" % self.api_version if self.api_version else "" - self.admin_url = "%sadmin/%s" % (self.base_url, api_version_path) + self.admin_url = self.base_url + "admin/%s" % api_version_path class OAuthAPI(appier.OAuth2API, API): From 98e9ebe0b9c5a74bd635c5c2683c7b13acbf88bb Mon Sep 17 00:00:00 2001 From: Nuno Silva Date: Fri, 13 Jan 2023 12:45:43 +0000 Subject: [PATCH 7/7] feat: create `_build_admin_url` --- src/shopify/base.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/shopify/base.py b/src/shopify/base.py index d9e0b89..278cdfc 100644 --- a/src/shopify/base.py +++ b/src/shopify/base.py @@ -188,6 +188,10 @@ def verify_signature(self, signature, data, key = None, base_64 = True): message = "Request signature is not valid", exception = appier.SecurityError ) + + def _build_admin_url(self): + api_version_path = "api/%s/" % self.api_version if self.api_version else "" + return self.base_url + "admin/%s" % api_version_path def _build_url(self): if not self.api_key: @@ -200,9 +204,7 @@ def _build_url(self): self.api_key, self.password, self.store_url ) self.website_url = "http://%s/" % (self.website_url or self.store_url) - - api_version_path = "api/%s/" % self.api_version if self.api_version else "" - self.admin_url = self.base_url + "admin/%s" % api_version_path + self.admin_url = self._build_admin_url() class OAuthAPI(appier.OAuth2API, API): @@ -257,6 +259,7 @@ def _build_url(self): if not self.store_url: raise appier.OperationalError(message = "No store URL provided") self.base_url = "https://%s/" % self.store_url + self.admin_url = self._build_admin_url() def _fetch_many( self,