dbbyte 0.1.0__tar.gz → 0.2.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.
- {dbbyte-0.1.0/src/dbbyte.egg-info → dbbyte-0.2.0}/PKG-INFO +25 -2
- {dbbyte-0.1.0 → dbbyte-0.2.0}/README.md +169 -146
- {dbbyte-0.1.0 → dbbyte-0.2.0}/pyproject.toml +1 -1
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/__init__.py +77 -49
- dbbyte-0.2.0/src/dbbyte/buckets.py +64 -0
- dbbyte-0.2.0/src/dbbyte/cdn.py +26 -0
- dbbyte-0.2.0/src/dbbyte/data.py +63 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/objects.py +155 -128
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/types.py +160 -94
- {dbbyte-0.1.0 → dbbyte-0.2.0/src/dbbyte.egg-info}/PKG-INFO +25 -2
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte.egg-info/SOURCES.txt +5 -1
- dbbyte-0.2.0/tests/test_api.py +95 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/LICENSE +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/setup.cfg +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/client.py +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/errors.py +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/py.typed +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte/utils.py +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte.egg-info/dependency_links.txt +0 -0
- {dbbyte-0.1.0 → dbbyte-0.2.0}/src/dbbyte.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbbyte
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Official DBByte SDK for Python
|
|
5
5
|
Author: DBByte, LLC
|
|
6
6
|
License: MIT
|
|
@@ -21,7 +21,7 @@ Official DBByte SDK for Python. A thin, typed wrapper over the DBByte v1 REST AP
|
|
|
21
21
|
|
|
22
22
|
## Status
|
|
23
23
|
|
|
24
|
-
Covers object storage
|
|
24
|
+
Covers object storage (upload, list, download, delete, versioning, signed URLs, signed upload URLs), bucket management, CDN cache purge, and Serve data APIs. There is no analytics API.
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -161,3 +161,26 @@ except DBByteError as e:
|
|
|
161
161
|
## License
|
|
162
162
|
|
|
163
163
|
MIT
|
|
164
|
+
|
|
165
|
+
## Buckets, cache purge, upload links, and data APIs
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
db.buckets.list()
|
|
169
|
+
db.buckets.create("Media", "us-east", is_public=True)
|
|
170
|
+
db.buckets.update("media", versioning_enabled=True)
|
|
171
|
+
db.buckets.delete("media")
|
|
172
|
+
|
|
173
|
+
# Needs a write-permission API key.
|
|
174
|
+
db.cdn.purge("media", scope="file", path="logo.png")
|
|
175
|
+
db.cdn.purge("media", scope="bucket")
|
|
176
|
+
|
|
177
|
+
# Direct browser uploads without exposing your API key.
|
|
178
|
+
link = db.objects.upload_url("media", "photo.jpg", content_type="image/jpeg", size=2_000_000)
|
|
179
|
+
|
|
180
|
+
# Serve data APIs
|
|
181
|
+
db.data.get("people", limit=20, filters={"city": "Oslo"})
|
|
182
|
+
db.data.create("people", {"name": "Ada"})
|
|
183
|
+
db.data.update("people", "record-id", {"name": "Ada L."})
|
|
184
|
+
db.data.append_text("notes", "New line")
|
|
185
|
+
db.data.delete("people", "record-id")
|
|
186
|
+
```
|
|
@@ -1,146 +1,169 @@
|
|
|
1
|
-
# dbbyte
|
|
2
|
-
|
|
3
|
-
Official DBByte SDK for Python. A thin, typed wrapper over the DBByte v1 REST API. Standard library only - no dependencies.
|
|
4
|
-
|
|
5
|
-
## Status
|
|
6
|
-
|
|
7
|
-
Covers object storage
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
pip install dbbyte
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Requires Python 3.9+.
|
|
16
|
-
|
|
17
|
-
## Usage
|
|
18
|
-
|
|
19
|
-
```python
|
|
20
|
-
import os
|
|
21
|
-
from dbbyte import DBByte
|
|
22
|
-
|
|
23
|
-
db = DBByte(api_key=os.environ["DBBYTE_API_KEY"])
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
| Argument | Type | Description |
|
|
27
|
-
| --- | --- | --- |
|
|
28
|
-
| `api_key` | `str` | Your DBByte API key. Required. |
|
|
29
|
-
| `base_url` | `str` | Override the API base URL. Defaults to `https://api.dbbyte.com`. |
|
|
30
|
-
| `timeout` | `float` | Request timeout in seconds. Defaults to `30.0`. |
|
|
31
|
-
|
|
32
|
-
### Health check
|
|
33
|
-
|
|
34
|
-
```python
|
|
35
|
-
status = db.health()
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Upload
|
|
39
|
-
|
|
40
|
-
```python
|
|
41
|
-
with open("logo.png", "rb") as f:
|
|
42
|
-
body = f.read()
|
|
43
|
-
|
|
44
|
-
obj = db.objects.upload(
|
|
45
|
-
"my-bucket",
|
|
46
|
-
key="images/logo.png",
|
|
47
|
-
body=body,
|
|
48
|
-
content_type="image/png",
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
# Re-uploading the same name fails with 409 unless you opt in:
|
|
52
|
-
db.objects.upload("my-bucket", key="images/logo.png", body=body, overwrite=True)
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### List
|
|
56
|
-
|
|
57
|
-
```python
|
|
58
|
-
result = db.objects.list("my-bucket")
|
|
59
|
-
files = result["files"]
|
|
60
|
-
|
|
61
|
-
# Paginate:
|
|
62
|
-
next_page = db.objects.list("my-bucket", cursor=result["nextCursor"])
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Download
|
|
66
|
-
|
|
67
|
-
```python
|
|
68
|
-
result = db.objects.download("my-bucket", "images/logo.png")
|
|
69
|
-
data = result["data"] # bytes
|
|
70
|
-
|
|
71
|
-
# A specific historical version:
|
|
72
|
-
db.objects.download("my-bucket", "images/logo.png", version="some-version-key")
|
|
73
|
-
|
|
74
|
-
# Every version, zipped:
|
|
75
|
-
zip_result = db.objects.download_all_versions("my-bucket", "images/logo.png")
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Versions
|
|
79
|
-
|
|
80
|
-
```python
|
|
81
|
-
result = db.objects.versions("my-bucket", "images/logo.png")
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Signed URLs
|
|
85
|
-
|
|
86
|
-
```python
|
|
87
|
-
result = db.objects.sign("my-bucket", "images/logo.png", expires_in=3600) # max 604800 (7 days)
|
|
88
|
-
url = result["url"]
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Delete
|
|
92
|
-
|
|
93
|
-
```python
|
|
94
|
-
db.objects.delete("my-bucket", "images/logo.png")
|
|
95
|
-
|
|
96
|
-
# A specific version only:
|
|
97
|
-
db.objects.delete("my-bucket", "images/logo.png", version="some-version-key")
|
|
98
|
-
|
|
99
|
-
# Every version, permanently:
|
|
100
|
-
db.objects.delete_all_versions("my-bucket", "images/logo.png")
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Error handling
|
|
104
|
-
|
|
105
|
-
All SDK methods raise a `DBByteError` on API errors.
|
|
106
|
-
|
|
107
|
-
```python
|
|
108
|
-
from dbbyte import DBByte, DBByteError
|
|
109
|
-
|
|
110
|
-
db = DBByte(api_key=os.environ["DBBYTE_API_KEY"])
|
|
111
|
-
|
|
112
|
-
try:
|
|
113
|
-
db.objects.download("my-bucket", "does-not-exist.png")
|
|
114
|
-
except DBByteError as e:
|
|
115
|
-
print(e.status) # e.g. 404
|
|
116
|
-
print(e.message) # e.g. "File not found"
|
|
117
|
-
print(e.hint) # present on some errors, e.g. upload conflicts
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Rate limits vs quota limits
|
|
121
|
-
|
|
122
|
-
A `429` can mean two different things, and only one is worth retrying:
|
|
123
|
-
|
|
124
|
-
- **Burst rate limit** - `e.retry_after` is set (seconds until you can retry). Transient, safe to retry after that delay.
|
|
125
|
-
- **Plan API request quota exceeded** - `e.retry_after` is `None`. Retrying immediately won't help; you're out of quota until your billing period resets or you upgrade.
|
|
126
|
-
|
|
127
|
-
```python
|
|
128
|
-
except DBByteError as e:
|
|
129
|
-
if e.status == 429:
|
|
130
|
-
if e.retry_after is not None:
|
|
131
|
-
# wait e.retry_after seconds, then retry
|
|
132
|
-
pass
|
|
133
|
-
else:
|
|
134
|
-
# plan quota exceeded, don't retry blindly
|
|
135
|
-
pass
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Known limitations
|
|
139
|
-
|
|
140
|
-
- **Upload allowed types**: the server enforces a fixed MIME type whitelist (common image, video, audio, PDF, Word, and plain-text formats). Uploading anything else returns a `400` from the server - the SDK doesn't pre-validate this client-side.
|
|
141
|
-
- **50MB max file size**, enforced server-side.
|
|
142
|
-
- **Synchronous only.** No async client yet.
|
|
143
|
-
|
|
144
|
-
## License
|
|
145
|
-
|
|
146
|
-
MIT
|
|
1
|
+
# dbbyte
|
|
2
|
+
|
|
3
|
+
Official DBByte SDK for Python. A thin, typed wrapper over the DBByte v1 REST API. Standard library only - no dependencies.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Covers object storage (upload, list, download, delete, versioning, signed URLs, signed upload URLs), bucket management, CDN cache purge, and Serve data APIs. There is no analytics API.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install dbbyte
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Python 3.9+.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import os
|
|
21
|
+
from dbbyte import DBByte
|
|
22
|
+
|
|
23
|
+
db = DBByte(api_key=os.environ["DBBYTE_API_KEY"])
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Argument | Type | Description |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `api_key` | `str` | Your DBByte API key. Required. |
|
|
29
|
+
| `base_url` | `str` | Override the API base URL. Defaults to `https://api.dbbyte.com`. |
|
|
30
|
+
| `timeout` | `float` | Request timeout in seconds. Defaults to `30.0`. |
|
|
31
|
+
|
|
32
|
+
### Health check
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
status = db.health()
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Upload
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
with open("logo.png", "rb") as f:
|
|
42
|
+
body = f.read()
|
|
43
|
+
|
|
44
|
+
obj = db.objects.upload(
|
|
45
|
+
"my-bucket",
|
|
46
|
+
key="images/logo.png",
|
|
47
|
+
body=body,
|
|
48
|
+
content_type="image/png",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Re-uploading the same name fails with 409 unless you opt in:
|
|
52
|
+
db.objects.upload("my-bucket", key="images/logo.png", body=body, overwrite=True)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### List
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
result = db.objects.list("my-bucket")
|
|
59
|
+
files = result["files"]
|
|
60
|
+
|
|
61
|
+
# Paginate:
|
|
62
|
+
next_page = db.objects.list("my-bucket", cursor=result["nextCursor"])
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Download
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
result = db.objects.download("my-bucket", "images/logo.png")
|
|
69
|
+
data = result["data"] # bytes
|
|
70
|
+
|
|
71
|
+
# A specific historical version:
|
|
72
|
+
db.objects.download("my-bucket", "images/logo.png", version="some-version-key")
|
|
73
|
+
|
|
74
|
+
# Every version, zipped:
|
|
75
|
+
zip_result = db.objects.download_all_versions("my-bucket", "images/logo.png")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Versions
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
result = db.objects.versions("my-bucket", "images/logo.png")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Signed URLs
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
result = db.objects.sign("my-bucket", "images/logo.png", expires_in=3600) # max 604800 (7 days)
|
|
88
|
+
url = result["url"]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Delete
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
db.objects.delete("my-bucket", "images/logo.png")
|
|
95
|
+
|
|
96
|
+
# A specific version only:
|
|
97
|
+
db.objects.delete("my-bucket", "images/logo.png", version="some-version-key")
|
|
98
|
+
|
|
99
|
+
# Every version, permanently:
|
|
100
|
+
db.objects.delete_all_versions("my-bucket", "images/logo.png")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Error handling
|
|
104
|
+
|
|
105
|
+
All SDK methods raise a `DBByteError` on API errors.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from dbbyte import DBByte, DBByteError
|
|
109
|
+
|
|
110
|
+
db = DBByte(api_key=os.environ["DBBYTE_API_KEY"])
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
db.objects.download("my-bucket", "does-not-exist.png")
|
|
114
|
+
except DBByteError as e:
|
|
115
|
+
print(e.status) # e.g. 404
|
|
116
|
+
print(e.message) # e.g. "File not found"
|
|
117
|
+
print(e.hint) # present on some errors, e.g. upload conflicts
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Rate limits vs quota limits
|
|
121
|
+
|
|
122
|
+
A `429` can mean two different things, and only one is worth retrying:
|
|
123
|
+
|
|
124
|
+
- **Burst rate limit** - `e.retry_after` is set (seconds until you can retry). Transient, safe to retry after that delay.
|
|
125
|
+
- **Plan API request quota exceeded** - `e.retry_after` is `None`. Retrying immediately won't help; you're out of quota until your billing period resets or you upgrade.
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
except DBByteError as e:
|
|
129
|
+
if e.status == 429:
|
|
130
|
+
if e.retry_after is not None:
|
|
131
|
+
# wait e.retry_after seconds, then retry
|
|
132
|
+
pass
|
|
133
|
+
else:
|
|
134
|
+
# plan quota exceeded, don't retry blindly
|
|
135
|
+
pass
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Known limitations
|
|
139
|
+
|
|
140
|
+
- **Upload allowed types**: the server enforces a fixed MIME type whitelist (common image, video, audio, PDF, Word, and plain-text formats). Uploading anything else returns a `400` from the server - the SDK doesn't pre-validate this client-side.
|
|
141
|
+
- **50MB max file size**, enforced server-side.
|
|
142
|
+
- **Synchronous only.** No async client yet.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
|
147
|
+
|
|
148
|
+
## Buckets, cache purge, upload links, and data APIs
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
db.buckets.list()
|
|
152
|
+
db.buckets.create("Media", "us-east", is_public=True)
|
|
153
|
+
db.buckets.update("media", versioning_enabled=True)
|
|
154
|
+
db.buckets.delete("media")
|
|
155
|
+
|
|
156
|
+
# Needs a write-permission API key.
|
|
157
|
+
db.cdn.purge("media", scope="file", path="logo.png")
|
|
158
|
+
db.cdn.purge("media", scope="bucket")
|
|
159
|
+
|
|
160
|
+
# Direct browser uploads without exposing your API key.
|
|
161
|
+
link = db.objects.upload_url("media", "photo.jpg", content_type="image/jpeg", size=2_000_000)
|
|
162
|
+
|
|
163
|
+
# Serve data APIs
|
|
164
|
+
db.data.get("people", limit=20, filters={"city": "Oslo"})
|
|
165
|
+
db.data.create("people", {"name": "Ada"})
|
|
166
|
+
db.data.update("people", "record-id", {"name": "Ada L."})
|
|
167
|
+
db.data.append_text("notes", "New line")
|
|
168
|
+
db.data.delete("people", "record-id")
|
|
169
|
+
```
|
|
@@ -1,49 +1,77 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
|
-
from .client import DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DBByteClient
|
|
6
|
-
from .errors import DBByteError
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from .client import DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DBByteClient
|
|
6
|
+
from .errors import DBByteError
|
|
7
|
+
from .buckets import BucketsResource
|
|
8
|
+
from .cdn import CdnResource
|
|
9
|
+
from .data import DataResource
|
|
10
|
+
from .objects import ObjectsResource
|
|
11
|
+
from .types import (
|
|
12
|
+
AppendTextResponse,
|
|
13
|
+
Bucket,
|
|
14
|
+
BucketDeleteResponse,
|
|
15
|
+
BucketListResponse,
|
|
16
|
+
BucketResponse,
|
|
17
|
+
DataDeleteResponse,
|
|
18
|
+
DataPage,
|
|
19
|
+
DataRecord,
|
|
20
|
+
DataRecordResponse,
|
|
21
|
+
PurgeResponse,
|
|
22
|
+
UploadUrlResponse,
|
|
23
|
+
DeleteResponse,
|
|
24
|
+
DownloadResult,
|
|
25
|
+
FileListItem,
|
|
26
|
+
FileVersionPreview,
|
|
27
|
+
HealthResponse,
|
|
28
|
+
ListObjectsResponse,
|
|
29
|
+
ListVersionsResponse,
|
|
30
|
+
SignedUrlResponse,
|
|
31
|
+
UploadedObject,
|
|
32
|
+
VersionEntry,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"DBByte",
|
|
37
|
+
"DBByteError",
|
|
38
|
+
"HealthResponse",
|
|
39
|
+
"UploadedObject",
|
|
40
|
+
"ListObjectsResponse",
|
|
41
|
+
"FileListItem",
|
|
42
|
+
"FileVersionPreview",
|
|
43
|
+
"SignedUrlResponse",
|
|
44
|
+
"ListVersionsResponse",
|
|
45
|
+
"VersionEntry",
|
|
46
|
+
"DeleteResponse",
|
|
47
|
+
"DownloadResult",
|
|
48
|
+
"Bucket",
|
|
49
|
+
"BucketListResponse",
|
|
50
|
+
"BucketResponse",
|
|
51
|
+
"BucketDeleteResponse",
|
|
52
|
+
"PurgeResponse",
|
|
53
|
+
"DataRecord",
|
|
54
|
+
"DataPage",
|
|
55
|
+
"DataRecordResponse",
|
|
56
|
+
"AppendTextResponse",
|
|
57
|
+
"DataDeleteResponse",
|
|
58
|
+
"UploadUrlResponse",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class DBByte:
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
api_key: str,
|
|
66
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
67
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
68
|
+
) -> None:
|
|
69
|
+
self._client = DBByteClient(api_key, base_url, timeout)
|
|
70
|
+
self.buckets = BucketsResource(self._client)
|
|
71
|
+
self.cdn = CdnResource(self._client)
|
|
72
|
+
self.data = DataResource(self._client)
|
|
73
|
+
self.objects = ObjectsResource(self._client)
|
|
74
|
+
|
|
75
|
+
def health(self) -> HealthResponse:
|
|
76
|
+
result = self._client.request_json("GET", "/health")
|
|
77
|
+
return result # type: ignore[no-any-return]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
from .client import DBByteClient
|
|
7
|
+
from .types import (
|
|
8
|
+
BucketDeleteResponse,
|
|
9
|
+
BucketListResponse,
|
|
10
|
+
BucketResponse,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BucketsResource:
|
|
15
|
+
def __init__(self, client: DBByteClient) -> None:
|
|
16
|
+
self._client = client
|
|
17
|
+
|
|
18
|
+
def list(self) -> BucketListResponse:
|
|
19
|
+
return self._client.request_json("GET", "/buckets") # type: ignore[no-any-return]
|
|
20
|
+
|
|
21
|
+
def get(self, slug: str) -> BucketResponse:
|
|
22
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
23
|
+
"GET", f"/buckets/{quote(slug, safe='')}"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def create(
|
|
27
|
+
self,
|
|
28
|
+
name: str,
|
|
29
|
+
region: str,
|
|
30
|
+
replica_count: Optional[int] = None,
|
|
31
|
+
is_public: Optional[bool] = None,
|
|
32
|
+
cdn: Optional[bool] = None,
|
|
33
|
+
) -> BucketResponse:
|
|
34
|
+
body: Dict[str, Any] = {"name": name, "region": region}
|
|
35
|
+
if replica_count is not None:
|
|
36
|
+
body["replicaCount"] = replica_count
|
|
37
|
+
if is_public is not None:
|
|
38
|
+
body["isPublic"] = is_public
|
|
39
|
+
if cdn is not None:
|
|
40
|
+
body["cdn"] = cdn
|
|
41
|
+
return self._client.request_json("POST", "/buckets", body=body) # type: ignore[no-any-return]
|
|
42
|
+
|
|
43
|
+
def update(
|
|
44
|
+
self,
|
|
45
|
+
slug: str,
|
|
46
|
+
versioning_enabled: Optional[bool] = None,
|
|
47
|
+
is_public: Optional[bool] = None,
|
|
48
|
+
cdn: Optional[bool] = None,
|
|
49
|
+
) -> BucketResponse:
|
|
50
|
+
body: Dict[str, Any] = {}
|
|
51
|
+
if versioning_enabled is not None:
|
|
52
|
+
body["versioningEnabled"] = versioning_enabled
|
|
53
|
+
if is_public is not None:
|
|
54
|
+
body["isPublic"] = is_public
|
|
55
|
+
if cdn is not None:
|
|
56
|
+
body["cdn"] = cdn
|
|
57
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
58
|
+
"PATCH", f"/buckets/{quote(slug, safe='')}", body=body
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def delete(self, slug: str) -> BucketDeleteResponse:
|
|
62
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
63
|
+
"DELETE", f"/buckets/{quote(slug, safe='')}"
|
|
64
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from .client import DBByteClient
|
|
6
|
+
from .types import PurgeResponse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CdnResource:
|
|
10
|
+
def __init__(self, client: DBByteClient) -> None:
|
|
11
|
+
self._client = client
|
|
12
|
+
|
|
13
|
+
def purge(
|
|
14
|
+
self, slug: str, scope: str = "bucket", path: Optional[str] = None
|
|
15
|
+
) -> PurgeResponse:
|
|
16
|
+
"""Purge one public file (scope="file", with path) or every public
|
|
17
|
+
file in a bucket (scope="bucket"). The API key needs write
|
|
18
|
+
permission."""
|
|
19
|
+
if scope not in ("file", "bucket"):
|
|
20
|
+
raise ValueError('scope must be "file" or "bucket"')
|
|
21
|
+
if scope == "file" and not path:
|
|
22
|
+
raise ValueError('path is required when scope is "file"')
|
|
23
|
+
body: Dict[str, Any] = {"slug": slug, "scope": scope}
|
|
24
|
+
if path:
|
|
25
|
+
body["path"] = path
|
|
26
|
+
return self._client.request_json("POST", "/purge", body=body) # type: ignore[no-any-return]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
from .client import DBByteClient
|
|
7
|
+
from .types import (
|
|
8
|
+
AppendTextResponse,
|
|
9
|
+
DataDeleteResponse,
|
|
10
|
+
DataPage,
|
|
11
|
+
DataRecord,
|
|
12
|
+
DataRecordResponse,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _seg(value: str) -> str:
|
|
17
|
+
return quote(value, safe="")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DataResource:
|
|
21
|
+
"""Serve data APIs published from CSV, JSON, or Google Docs."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, client: DBByteClient) -> None:
|
|
24
|
+
self._client = client
|
|
25
|
+
|
|
26
|
+
def get(
|
|
27
|
+
self,
|
|
28
|
+
slug: str,
|
|
29
|
+
fields: Optional[List[str]] = None,
|
|
30
|
+
cursor: Optional[str] = None,
|
|
31
|
+
limit: Optional[int] = None,
|
|
32
|
+
filters: Optional[Dict[str, str]] = None,
|
|
33
|
+
) -> DataPage:
|
|
34
|
+
query: Dict[str, Any] = dict(filters or {})
|
|
35
|
+
if fields:
|
|
36
|
+
query["fields"] = ",".join(fields)
|
|
37
|
+
query["cursor"] = cursor
|
|
38
|
+
query["limit"] = limit
|
|
39
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
40
|
+
"GET", f"/v1/data/{_seg(slug)}", query=query
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def create(self, slug: str, record: DataRecord) -> DataRecordResponse:
|
|
44
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
45
|
+
"POST", f"/v1/data/{_seg(slug)}", body=record
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def append_text(self, slug: str, text: str) -> AppendTextResponse:
|
|
49
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
50
|
+
"POST", f"/v1/data/{_seg(slug)}", body={"appendText": text}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def update(
|
|
54
|
+
self, slug: str, record_id: str, patch: DataRecord
|
|
55
|
+
) -> DataRecordResponse:
|
|
56
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
57
|
+
"PATCH", f"/v1/data/{_seg(slug)}/{_seg(record_id)}", body=patch
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def delete(self, slug: str, record_id: str) -> DataDeleteResponse:
|
|
61
|
+
return self._client.request_json( # type: ignore[no-any-return]
|
|
62
|
+
"DELETE", f"/v1/data/{_seg(slug)}/{_seg(record_id)}"
|
|
63
|
+
)
|