mycommerce-platform 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mycommerce_platform-0.1.0/.gitignore +94 -0
- mycommerce_platform-0.1.0/PKG-INFO +64 -0
- mycommerce_platform-0.1.0/README.md +40 -0
- mycommerce_platform-0.1.0/pyproject.toml +41 -0
- mycommerce_platform-0.1.0/src/mycommerce/__init__.py +6 -0
- mycommerce_platform-0.1.0/src/mycommerce/client.py +83 -0
- mycommerce_platform-0.1.0/tests/test_client.py +22 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
pnpm-debug.log*
|
|
8
|
+
lerna-debug.log*
|
|
9
|
+
|
|
10
|
+
node_modules
|
|
11
|
+
dist
|
|
12
|
+
dist-ssr
|
|
13
|
+
*.local
|
|
14
|
+
|
|
15
|
+
# Editor directories and files
|
|
16
|
+
.vscode/*
|
|
17
|
+
!.vscode/extensions.json
|
|
18
|
+
!.vscode/settings.json
|
|
19
|
+
.idea
|
|
20
|
+
.DS_Store
|
|
21
|
+
*.suo
|
|
22
|
+
*.ntvs*
|
|
23
|
+
*.njsproj
|
|
24
|
+
*.sln
|
|
25
|
+
*.sw?
|
|
26
|
+
|
|
27
|
+
.thumbs.db
|
|
28
|
+
|
|
29
|
+
# Quasar core related directories
|
|
30
|
+
.quasar
|
|
31
|
+
/quasar.config.*.temporary.compiled*
|
|
32
|
+
|
|
33
|
+
# Cordova related directories and files
|
|
34
|
+
/src-cordova/node_modules
|
|
35
|
+
/src-cordova/platforms
|
|
36
|
+
/src-cordova/plugins
|
|
37
|
+
/src-cordova/www
|
|
38
|
+
|
|
39
|
+
# Capacitor related directories and files
|
|
40
|
+
/src-capacitor/www
|
|
41
|
+
/src-capacitor/node_modules
|
|
42
|
+
|
|
43
|
+
# BEX related directories and files
|
|
44
|
+
/src-bex/www
|
|
45
|
+
/src-bex/js/core
|
|
46
|
+
|
|
47
|
+
# Nuxt dev/build outputs
|
|
48
|
+
.output
|
|
49
|
+
.data
|
|
50
|
+
.nuxt
|
|
51
|
+
.nitro
|
|
52
|
+
.cache
|
|
53
|
+
|
|
54
|
+
# Local env files
|
|
55
|
+
.env
|
|
56
|
+
.env.*
|
|
57
|
+
.env.local*
|
|
58
|
+
!.env.example
|
|
59
|
+
|
|
60
|
+
# Misc files
|
|
61
|
+
.fleet
|
|
62
|
+
.tmp
|
|
63
|
+
*.tmp*
|
|
64
|
+
*.sublime-project
|
|
65
|
+
*.sublime-workspace
|
|
66
|
+
UserInterfaceState.xcuserstate
|
|
67
|
+
$RECYCLE.BIN/
|
|
68
|
+
log.txt
|
|
69
|
+
.coverage
|
|
70
|
+
*.sqlite*
|
|
71
|
+
|
|
72
|
+
# IDE and build directories
|
|
73
|
+
/.ionic
|
|
74
|
+
/.sass-cache
|
|
75
|
+
/.sourcemaps
|
|
76
|
+
/.versions
|
|
77
|
+
/.vscode/*
|
|
78
|
+
!/.vscode/extensions.json
|
|
79
|
+
!/.vscode/settings.json
|
|
80
|
+
/coverage
|
|
81
|
+
/platforms
|
|
82
|
+
/plugins
|
|
83
|
+
/www
|
|
84
|
+
|
|
85
|
+
# Python
|
|
86
|
+
__pycache__/
|
|
87
|
+
|
|
88
|
+
# Tests
|
|
89
|
+
*/coverage/
|
|
90
|
+
__snapshots__/
|
|
91
|
+
|
|
92
|
+
*~
|
|
93
|
+
Thumbs.db
|
|
94
|
+
.pytest_cache
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mycommerce-platform
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Small, dependency-free Python client for the MyCommerce API gateway
|
|
5
|
+
Project-URL: Homepage, https://github.com/bayzed123/mycommerce
|
|
6
|
+
Project-URL: Repository, https://github.com/bayzed123/mycommerce
|
|
7
|
+
Project-URL: Issues, https://github.com/bayzed123/mycommerce/issues
|
|
8
|
+
Author: MyCommerce Contributors
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: api,cart,ecommerce,mycommerce,payments,shop
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# mycommerce-platform
|
|
26
|
+
|
|
27
|
+
`mycommerce-platform` is the small, dependency-free Python client for the MyCommerce Cloudflare Worker gateway. It gives Python applications one stable base URL while the gateway routes requests to the Shop API, Cart API, and Go Purchase service.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install mycommerce-platform
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from mycommerce import MyCommerceClient
|
|
39
|
+
|
|
40
|
+
client = MyCommerceClient(
|
|
41
|
+
"https://mycommerce-api-gateway.<your-subdomain>.workers.dev",
|
|
42
|
+
token="optional-jwt-token",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
print(client.health())
|
|
46
|
+
products = client.shop("api/v1/shop/")
|
|
47
|
+
cart = client.cart("cart/v1/")
|
|
48
|
+
payment = client.purchase("create", method="POST", payload={"sessionId": "session-123"})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The package only uses Python's standard library. It does not contain Stripe, database, Django, or Cloudflare credentials. Authentication is supplied through the optional bearer token and service authorization remains a gateway/backend responsibility.
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
cd packages/mycommerce-platform
|
|
57
|
+
python -m venv .venv
|
|
58
|
+
. .venv/bin/activate
|
|
59
|
+
python -m pip install -U pip pytest
|
|
60
|
+
pytest
|
|
61
|
+
python -m build
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The package is published from GitHub Actions when a `mycommerce-platform-v*` tag is pushed. Configure a PyPI Trusted Publisher for this GitHub repository before creating the first release.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# mycommerce-platform
|
|
2
|
+
|
|
3
|
+
`mycommerce-platform` is the small, dependency-free Python client for the MyCommerce Cloudflare Worker gateway. It gives Python applications one stable base URL while the gateway routes requests to the Shop API, Cart API, and Go Purchase service.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install mycommerce-platform
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from mycommerce import MyCommerceClient
|
|
15
|
+
|
|
16
|
+
client = MyCommerceClient(
|
|
17
|
+
"https://mycommerce-api-gateway.<your-subdomain>.workers.dev",
|
|
18
|
+
token="optional-jwt-token",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
print(client.health())
|
|
22
|
+
products = client.shop("api/v1/shop/")
|
|
23
|
+
cart = client.cart("cart/v1/")
|
|
24
|
+
payment = client.purchase("create", method="POST", payload={"sessionId": "session-123"})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The package only uses Python's standard library. It does not contain Stripe, database, Django, or Cloudflare credentials. Authentication is supplied through the optional bearer token and service authorization remains a gateway/backend responsibility.
|
|
28
|
+
|
|
29
|
+
## Development
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd packages/mycommerce-platform
|
|
33
|
+
python -m venv .venv
|
|
34
|
+
. .venv/bin/activate
|
|
35
|
+
python -m pip install -U pip pytest
|
|
36
|
+
pytest
|
|
37
|
+
python -m build
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The package is published from GitHub Actions when a `mycommerce-platform-v*` tag is pushed. Configure a PyPI Trusted Publisher for this GitHub repository before creating the first release.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mycommerce-platform"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Small, dependency-free Python client for the MyCommerce API gateway"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "MyCommerce Contributors" }]
|
|
13
|
+
keywords = ["mycommerce", "ecommerce", "api", "shop", "cart", "payments"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/bayzed123/mycommerce"
|
|
30
|
+
Repository = "https://github.com/bayzed123/mycommerce"
|
|
31
|
+
Issues = "https://github.com/bayzed123/mycommerce/issues"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel]
|
|
34
|
+
packages = ["src/mycommerce"]
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
|
38
|
+
addopts = "-q"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.version]
|
|
41
|
+
path = "src/mycommerce/__init__.py"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Small standard-library-only client for the MyCommerce API gateway."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any, Mapping
|
|
8
|
+
from urllib.error import HTTPError, URLError
|
|
9
|
+
from urllib.parse import urljoin
|
|
10
|
+
from urllib.request import Request, urlopen
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApiError(RuntimeError):
|
|
14
|
+
"""Raised when the gateway cannot return a successful JSON response."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, status: int, message: str, payload: Any = None) -> None:
|
|
17
|
+
super().__init__(f"MyCommerce API error {status}: {message}")
|
|
18
|
+
self.status = status
|
|
19
|
+
self.payload = payload
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class MyCommerceClient:
|
|
24
|
+
"""Client for the Worker gateway and its three backend routes.
|
|
25
|
+
|
|
26
|
+
The gateway is expected to expose `/shop/*`, `/cart/*`, and `/purchase/*`.
|
|
27
|
+
The client does not contain service credentials; authentication is supplied
|
|
28
|
+
through the optional bearer token at request time or construction time.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
base_url: str
|
|
32
|
+
token: str | None = None
|
|
33
|
+
timeout: float = 15.0
|
|
34
|
+
|
|
35
|
+
def _request(
|
|
36
|
+
self,
|
|
37
|
+
method: str,
|
|
38
|
+
route: str,
|
|
39
|
+
payload: Mapping[str, Any] | list[Any] | None = None,
|
|
40
|
+
*,
|
|
41
|
+
token: str | None = None,
|
|
42
|
+
) -> Any:
|
|
43
|
+
url = urljoin(self.base_url.rstrip("/") + "/", route.lstrip("/"))
|
|
44
|
+
headers = {"Accept": "application/json"}
|
|
45
|
+
auth_token = token or self.token
|
|
46
|
+
if auth_token:
|
|
47
|
+
headers["Authorization"] = f"Bearer {auth_token}"
|
|
48
|
+
body = None
|
|
49
|
+
if payload is not None:
|
|
50
|
+
headers["Content-Type"] = "application/json"
|
|
51
|
+
body = json.dumps(payload).encode("utf-8")
|
|
52
|
+
request = Request(url, data=body, headers=headers, method=method.upper())
|
|
53
|
+
try:
|
|
54
|
+
with urlopen(request, timeout=self.timeout) as response:
|
|
55
|
+
raw = response.read()
|
|
56
|
+
if not raw:
|
|
57
|
+
return None
|
|
58
|
+
return json.loads(raw.decode("utf-8"))
|
|
59
|
+
except HTTPError as error:
|
|
60
|
+
raw = error.read().decode("utf-8", errors="replace")
|
|
61
|
+
try:
|
|
62
|
+
payload_data = json.loads(raw) if raw else None
|
|
63
|
+
except json.JSONDecodeError:
|
|
64
|
+
payload_data = raw
|
|
65
|
+
raise ApiError(error.code, error.reason, payload_data) from error
|
|
66
|
+
except URLError as error:
|
|
67
|
+
raise ApiError(0, str(error.reason)) from error
|
|
68
|
+
|
|
69
|
+
def health(self) -> Any:
|
|
70
|
+
"""Return the gateway health response."""
|
|
71
|
+
return self._request("GET", "/health")
|
|
72
|
+
|
|
73
|
+
def shop(self, path: str = "", *, method: str = "GET", payload: Any = None) -> Any:
|
|
74
|
+
"""Call a path proxied to the Shop API."""
|
|
75
|
+
return self._request(method, f"/shop/{path.lstrip('/')}", payload)
|
|
76
|
+
|
|
77
|
+
def cart(self, path: str = "", *, method: str = "GET", payload: Any = None) -> Any:
|
|
78
|
+
"""Call a path proxied to the Cart API."""
|
|
79
|
+
return self._request(method, f"/cart/{path.lstrip('/')}", payload)
|
|
80
|
+
|
|
81
|
+
def purchase(self, path: str = "", *, method: str = "GET", payload: Any = None) -> Any:
|
|
82
|
+
"""Call a path proxied to the Go Purchase service."""
|
|
83
|
+
return self._request(method, f"/purchase/{path.lstrip('/')}", payload)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
from mycommerce import ApiError, MyCommerceClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_route_prefixes_are_forwarded_to_the_gateway():
|
|
7
|
+
client = MyCommerceClient("https://api.example.test")
|
|
8
|
+
with patch.object(client, "_request", return_value={"ok": True}) as request:
|
|
9
|
+
assert client.shop("api/v1/products") == {"ok": True}
|
|
10
|
+
assert client.cart("cart/v1/items", method="POST", payload={"id": 1}) == {"ok": True}
|
|
11
|
+
assert client.purchase("create", method="POST", payload={"total": 10}) == {"ok": True}
|
|
12
|
+
|
|
13
|
+
assert request.call_count == 3
|
|
14
|
+
assert request.call_args_list[0].args == ("GET", "/shop/api/v1/products", None)
|
|
15
|
+
assert request.call_args_list[1].args == ("POST", "/cart/cart/v1/items", {"id": 1})
|
|
16
|
+
assert request.call_args_list[2].args == ("POST", "/purchase/create", {"total": 10})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_api_error_exposes_status_and_payload():
|
|
20
|
+
error = ApiError(401, "Unauthorized", {"detail": "invalid token"})
|
|
21
|
+
assert error.status == 401
|
|
22
|
+
assert error.payload["detail"] == "invalid token"
|