-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a9bdc32
commit bce8805
Showing
5 changed files
with
463 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,101 @@ | ||
# shopify-client | ||
Python client for Shopify REST & GraphQL API | ||
## Installation | ||
|
||
```bash | ||
pip install -e git+https://github.com/groveco/shopify-client@main#egg=shopify_client | ||
``` | ||
|
||
## Usage | ||
|
||
### REST API | ||
|
||
```python | ||
from shopify_client import ShopifyClient | ||
|
||
# Initialize the client | ||
client = ShopifyClient(api_url='your_api_url', api_token='your_token', api_version='your_api_version') | ||
|
||
# Get a list of products | ||
products = client.products.all() | ||
|
||
# Get only specific fields | ||
products = client.products.all(fields="id,title") | ||
|
||
# Get limited amount of products | ||
products = client.products.all(limit=20) | ||
|
||
# Use pagination | ||
for page in client.products.all(paginate=True, limit=100) | ||
print(page) | ||
|
||
# Get specific product by id | ||
product = client.products.get(resource_id=1234) | ||
|
||
# List product metafields for product id | ||
metafields = client.products.metafields.all(resource_id=1234) | ||
|
||
# Get speficic metafield by id | ||
metafield = client.products.metafields.all(resource_id=1234, sub_resource_id=5678) | ||
|
||
# Create a product | ||
data = {"product":{"title":"Burton Custom Freestyle 151","body_html":"<strong>Good snowboard!</strong>","vendor":"Burton","product_type":"Snowboard","status":"draft"}} | ||
product = client.products.create(json=data) | ||
|
||
# Update product | ||
product = client.products.create(resource_id=1234, json=data) | ||
|
||
# Delete product | ||
deleted = client.products.delete(resource=1234) | ||
|
||
# Count of products | ||
count = client.products.count() | ||
|
||
# Cancel order | ||
order = client.orders.cancel(resource_id=1234) | ||
|
||
# Close order | ||
order = client.orders.close(resource_id=1234) | ||
|
||
``` | ||
|
||
### GraphQL API | ||
|
||
```python | ||
# List products | ||
query = ''' | ||
query products($page_size: Int = 100) { | ||
products(first: $page_size) { | ||
nodes { | ||
id | ||
title | ||
} | ||
} | ||
} | ||
''' | ||
response = client.graphql.query(query) | ||
|
||
# Limit page size | ||
response = client.graphql.query(query, variables={"page_size": 20}) | ||
|
||
# Use pagination. | ||
# Note that "pageIngo" block with at least "hasNextPage" & "startCursor" is required | ||
# $cursor value should be passed as "after" parameter | ||
query = ''' | ||
query products($page_size: Int = 100, $cursor: String) { | ||
products(first: $page_size, after: $cursor) { | ||
nodes { | ||
id | ||
title | ||
} | ||
pageInfo { | ||
hasNextPage | ||
startCursor | ||
} | ||
} | ||
} | ||
''' | ||
for page in client.graphql.query_paginated(query) | ||
print(page) | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from setuptools import setup, find_packages | ||
|
||
setup( | ||
name="shopify-client", | ||
version="0.1.0", | ||
description="Python client for Shopify REST and GraphQL API", | ||
long_description=open("README.md").read(), | ||
long_description_content_type="text/markdown", | ||
author="Anton Shutsik", | ||
author_email="[email protected]", | ||
url="https://github.com/groveco/shopify-client.git", | ||
packages=["shopify_client"], | ||
install_requires=[ | ||
"requests>=2.25.1", | ||
], | ||
extras_require={ | ||
"dev": [ | ||
"pytest", | ||
"pytest-cov", | ||
"flake8", | ||
"black", | ||
"sphinx", | ||
"pre-commit" | ||
] | ||
}, | ||
python_requires=">=3.7", | ||
classifiers=[ | ||
"Programming Language :: Python :: 3", | ||
"Programming Language :: Python :: 3.7", | ||
"Programming Language :: Python :: 3.8", | ||
"Programming Language :: Python :: 3.9", | ||
"Programming Language :: Python :: 3.10", | ||
"License :: OSI Approved :: MIT License", | ||
"Operating System :: OS Independent", | ||
"Intended Audience :: Developers", | ||
"Topic :: Software Development :: Libraries :: Python Modules", | ||
], | ||
project_urls={ | ||
"Documentation": "https://github.com/groveco/shopify-client", | ||
"Source": "https://github.com/groveco/shopify-client", | ||
"Tracker": "https://github.com/groveco/shopify-client/issues", | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import logging | ||
import time | ||
from urllib.parse import urljoin | ||
|
||
import requests | ||
|
||
from client.endpoint import DraftOrdersEndpoint, Endpoint, OrdersEndpoint | ||
from client.graphql import GraphQL | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
SHOPIFY_API_VERSION = "2024-10" | ||
|
||
|
||
class ShopifyClient(requests.Session): | ||
|
||
def __init__(self, api_url, api_token, api_version=SHOPIFY_API_VERSION): | ||
super().__init__() | ||
self.api_url = api_url | ||
self.api_version = api_version | ||
self.headers.update({"X-Shopify-Access-Token": api_token, "Content-Type": "application/json"}) | ||
|
||
# Access | ||
self.storefront_access_tokens = Endpoint(client=self, endpoint="storefront_access_tokens") | ||
|
||
# Billing | ||
self.application_charges = Endpoint(client=self, endpoint="application_charges") | ||
self.application_credits = Endpoint(client=self, endpoint="application_credits") | ||
self.recurring_application_charges = Endpoint(client=self, endpoint="recurring_application_charges") | ||
|
||
# Customers | ||
self.customers = Endpoint(client=self, endpoint="customers", metafields=True) | ||
# self.customer_addresses = ShopifyEndpoint(client=self, endpoint="customer_addresses") | ||
|
||
# Discounts | ||
self.price_rules = Endpoint(client=self, endpoint="price_rules") | ||
self.discount_codes = Endpoint(client=self, endpoint="discount_codes") | ||
|
||
# Events | ||
self.events = Endpoint(client=self, endpoint="events") | ||
|
||
# Gift Cards | ||
self.gift_cards = Endpoint(client=self, endpoint="gift_cards") | ||
|
||
# Inventory | ||
self.inventory_items = Endpoint(client=self, endpoint="inventory_items") | ||
self.inventory_levels = Endpoint(client=self, endpoint="inventory_levels") | ||
self.locations = Endpoint(client=self, endpoint="locations", metafields=True) | ||
|
||
# Marketing Event | ||
self.marketing_events = Endpoint(client=self, endpoint="marketing_events") | ||
|
||
# Mobile Support | ||
self.mobile_platform_applications = Endpoint(client=self, endpoint="mobile_platform_applications") | ||
|
||
# Online Store | ||
self.articles = Endpoint(client=self, endpoint="articles", metafields=True) | ||
self.blogs = Endpoint(client=self, endpoint="blogs", metafields=True) | ||
self.pages = Endpoint(client=self, endpoint="pages", metafields=True) | ||
|
||
# Orders | ||
self.checkouts = Endpoint(client=self, endpoint="checkouts") | ||
self.draft_orders = DraftOrdersEndpoint(client=self, endpoint="draft_orders", metafields=True) | ||
self.orders = OrdersEndpoint(client=self, endpoint="orders") | ||
|
||
# Plus | ||
self.users = Endpoint(client=self, endpoint="users") | ||
|
||
# Products | ||
self.collects = Endpoint(client=self, endpoint="collects") | ||
self.collections = Endpoint(client=self, endpoint="collections", metafields=True) | ||
self.custom_collections = Endpoint(client=self, endpoint="custom_collections") | ||
self.products = Endpoint(client=self, endpoint="products", metafields=True) | ||
self.products.images = Endpoint(client=self, endpoint="products", sub_endpoint="images") | ||
self.product_images = Endpoint(client=self, endpoint="product_images", metafields=True) | ||
self.smart_collections = Endpoint(client=self, endpoint="smart_collections", metafields=True) | ||
self.variants = Endpoint(client=self, endpoint="variants", metafields=True) | ||
|
||
# Sales Channels | ||
self.collections_listings = Endpoint(client=self, endpoint="collections_listings") | ||
self.checkouts = Endpoint(client=self, endpoint="checkouts") | ||
self.product_listings = Endpoint(client=self, endpoint="product_listings") | ||
self.resource_feedback = Endpoint(client=self, endpoint="resource_feedback") | ||
|
||
# Shipping and Fulfillment | ||
self.assigned_fulfillment_orders = Endpoint(client=self, endpoint="assigned_fulfillment_orders") | ||
# TODO: Implement Fulfillment | ||
self.fulfillment_orders = Endpoint(client=self, endpoint="fulfillment_orders") | ||
self.carrier_services = Endpoint(client=self, endpoint="carrier_services") | ||
self.fulfillments = Endpoint(client=self, endpoint="fulfillments") | ||
self.fulfillment_services = Endpoint(client=self, endpoint="fulfillment_services") | ||
|
||
# Shopify Payments | ||
self.shopify_payments = Endpoint(client=self, endpoint="shopify_payments") | ||
|
||
# Store Properties | ||
self.countries = Endpoint(client=self, endpoint="countries") | ||
self.currencies = Endpoint(client=self, endpoint="currencies") | ||
self.policies = Endpoint(client=self, endpoint="policies") | ||
self.shipping_zones = Endpoint(client=self, endpoint="shipping_zones") | ||
self.shop = Endpoint(client=self, endpoint="shop", metafields=True) | ||
|
||
# Tender Transactions | ||
self.tender_transactions = Endpoint(client=self, endpoint="tender_transactions") | ||
|
||
# Webhooks | ||
self.webhooks = Endpoint(client=self, endpoint="webhooks") | ||
|
||
# GraphQL | ||
self.graphql = GraphQL(client=self) | ||
|
||
def rate_limit(r, *args, **kwargs): | ||
if r.status_code == 429: | ||
logger.warning("Shopify service exceeds API call limit; will retry request in %s seconds" % r.headers.get("retry-after", 4)) | ||
time.sleep(float(r.headers.get("retry-after", 4))) | ||
return True | ||
|
||
self.hooks["response"].append(rate_limit) | ||
|
||
def request(self, method, url, *args, **kwargs): | ||
response = super().request(method, urljoin(f"{self.api_url}/admin/api/{self.api_version}/", url), *args, **kwargs) | ||
logger.info(f"Requesting {method} {url}: {response.status_code}") | ||
return response | ||
|
||
def parse_response(self, response): | ||
try: | ||
response.raise_for_status() | ||
except requests.exceptions.HTTPError as e: | ||
logger.warning(f"Failed to execute request: {response.text}") | ||
raise e | ||
return response.json() |
Oops, something went wrong.