# Shopify Python Library - SPyLib
The Shopify python library or SPyLib, simplifies the use of the Shopify
services such as the REST and GraphQL APIs as well as the OAuth authentication.
All of this is done **asynchronously using asyncio**.
Features:
* [Shopify admin API](#shopify-admin-api)
* [REST API](#rest-api)
* Rate limit handling per API key
* Retry mechanism for 5xx status codes from Shopify
* [GraphQL API](#graphql-api)
* [Shopify OAuth](#shopify-oauth)
* Provide FastAPI router ready to handle the OAuth process
* Handle the whole Oauth process
* Callback validation without the need to track nonce locally
* Provide post installation and post login function injection for full control
## Installation
```bash
pip install spylib
```
## Contributing
If you want to contribute a small change/feature, the best is to just create a PR with
your changes.
For bigger changes/features it's best to open an issue first and discuss it to agree
on the code organization and overall implementation before spending too much time on
the code, unless you want to keep it in your own forked repo.
### Setting up the development environment
We use the [python poetry](https://python-poetry.org/) package to manage this package.
Follow the official instructions to install poetry on your system then once you clone
this repository just just need to do the following to install the dependencies from
the development environment, as well as install `spylib` in
[editable mode](https://pip.pypa.io/en/stable/cli/pip_install/#install-editable):
```bash
poetry install
```
Then you can start monitoring the code for changes and run the test suite this way:
```bash
poetry shell
scripts/test_watch.sh
```
## Tutorial
### Shopify admin API
The Shopify API version is defined [here](spylib/constants.py) and is typically
the latest stable version.
#### Initialize store instance
All the API calls to Shopify are done using an instance of the `Store` class.
This class provides methods to perform Rest and GraphQL calls.
```python
from spylib import Store
store = Store.load(store_id='mystore1', name='mystore', access_token='shppa_7a1e466ab2a')
```
Each store instance is defined using a `store_id` identifying the store uniquely, a `name`
corresponding to the Shopify store subdomain such that `https://<name>.myshopify.com`, and
the `access_token` or offline token to be used in the calls.
#### Rest API
The `Store` instance tracks and handles the rate limit for Rest calls.
When the rate limit is hit (status code 429), the `Store` instance will estimate how quickly
the available calls are restored and it will randomly retry one of the calls that
were stalled.
Besides the rate limit, the `Store` will also automatically retry calls failing with a status
code 5xx which are due to failures of Shopify or the network. All other status codes are
assumed to be errors of the implementation and therefore shouldn't be needlessly retried but
instead raise an exception right away.
Example `GET` request to retrieve all the tags on a customer account:
```python
custid = 1234567890
customerjson = await store.shoprequest(
goodstatus=200,
method='get',
debug=f'Couldn\'t get tags for customer id {custid} from Shopify.',
endpoint=f'/customers.json?fields=tags&ids={custid}',
)
```
The `goodstatus` defines the expected successful status code. Any other status code will
be considered to be an error and will raise an exception.
In case the Rest call cannot go through, the `debug` message
is used to provide an app specific messaging along with the generic exception message.
The `method` and `endpoint` define the Rest API call with the `endpoint` being the last
part of the URL.
Example `POST` request to update tags on a customer account:
```python
custdata = await store.shoprequest(
goodstatus=200,
method='put',
debug=(f'Couldn\'t update tags for customer id {custid} from Shopify.'),
endpoint=f'/customers/{custid}.json',
json={'customer': {'id': custid, 'tags': 'mytag, yourtag, hertag, histag'}},
)
```
The body of the `POST` request is given as a dictionary to the `json` argument.
Similarly the headers can be passed as a dictionary to the `headers` argument.
The URL parameters can be passed as part of `endpoint` although it is preferrable
to provide a dictionary to the `params` argument.
#### GraphQL API
To perform a GraphQL call, one must simply provide the `query` string.
The `query` name of the argument matches the GraphQL specification for the field
name that corresponds to a string with the `query` or `mutation` code
in the GraphQL format.
The variables of the GraphQL query are passed as a dictionary to the `variables`
argument.
See this example retrieving the list of webhooks defined for a store with
a given callback URL:
```python
query = """query getWebhooks($callbackUrl: URL!) {
webhookSubscriptions(callbackUrl: $callbackUrl, first: 1) {
edges {
node {
id
callbackUrl
}
}
}
}"""
res = await store.execute_gql(
query=QUERY,
variables={'callbackUrl': 'https://my.app.com/webhooks'},
)
webhook_nodes = res['webhookSubscription']['edges']
```
### Shopify OAuth
Rather than reimplementing for each app the
[Shopify OAuth authentication](https://shopify.dev/tutorials/authenticate-with-oauth)
one can simple get a [FastAPI](https://fastapi.tiangolo.com/) router that provides
the install and callback endpoints ready to handle the whole OAuth process.
You just need to call `init_oauth_router` such that:
```python
from spylib.oauth import init_oauth_router, OfflineToken, OnlineToken
async def my_post_install(storename: str, offline_token: OfflineToken):
"""Function handling the offline token obtained at the end of the installation"""
# Store to database
pass
async def my_post_login(storename: str, online_token: OnlineToken):
"""Function handling the online token obtained at the end of the user login"""
# Store to database
pass
oauth_router = init_oauth_router(
app_scopes=['write_orders', 'write_products'],
user_scopes=['read_orders', 'write_products'],
public_domain='my.app.com',
private_key='KEY_FOR_OAUTH_JWT',
post_install=my_post_install,
post_login=my_post_login,
)
```
The `app_scopes` are for the offline token and the `user_scopes` for the online token.
The `public_domain` is used to set the callback URL used in the OAuth process.
This library uses a JWT encoded `nonce` to avoid the need for a database or some other
mechanism to track the `nonce`. This JWT has an expiration time and is unique for each
OAuth process making it a valid `nonce` mechanism.
The `private_key` parameter defines the key used to encode and decode this JWT.
The `post_install` and `post_login` provide a way to inject functions handling the
result of the installation and the login processes respectivaly. They are meant in
particular to record the offline and online tokens in your app's database.
They can be synchronous or asynchronous functions taking the storename and the token
as arguments.
```
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
共32个文件
py:20个
sh:2个
lock:1个
资源分类:Python库 所属语言:Python 资源全名:spylib-0.2.2.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
资源推荐
资源详情
资源评论
收起资源包目录
spylib-0.2.2.tar.gz (32个子文件)
spylib-0.2.2
PKG-INFO 7KB
poetry.lock 60KB
spylib
constants.py 128B
py.typed 0B
oauth
router.py 4KB
validations.py 2KB
__init__.py 151B
config.py 234B
tokens.py 2KB
redirects.py 2KB
__init__.py 160B
store.py 7KB
utils
domain.py 682B
httpclient.py 383B
hmac.py 1001B
misc.py 241B
__init__.py 347B
jwtoken.py 3KB
exceptions.py 663B
pyproject.toml 1KB
tests
test_oauth.py 6KB
conftest.py 435B
test_store.py 7KB
LICENSE 1KB
mypy.ini 75B
setup.py 523B
.gitignore 2KB
README.md 7KB
scripts
test_watch.sh 187B
test_suite.sh 634B
.flake8 104B
CODEOWNERS 13B
共 32 条
- 1
资源评论
挣扎的蓝藻
- 粉丝: 13w+
- 资源: 15万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功