devnomads 0.1.2__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.
- {devnomads-0.1.2 → devnomads-0.2.0}/.gitlab-ci.yml +1 -0
- devnomads-0.2.0/Makefile +27 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/PKG-INFO +70 -4
- {devnomads-0.1.2 → devnomads-0.2.0}/README.md +69 -3
- devnomads-0.2.0/openapi.json +3629 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/pyproject.toml +1 -1
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/__init__.py +8 -1
- devnomads-0.2.0/src/devnomads/api/_services.py +916 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/api/client.py +5 -2
- devnomads-0.2.0/tests/test_services.py +149 -0
- devnomads-0.2.0/tools/generate.py +285 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/uv.lock +1 -1
- {devnomads-0.1.2 → devnomads-0.2.0}/.flake8 +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/.gitignore +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/__init__.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/challenge_server.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/client.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/dns01.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/errors.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/http01.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/keys.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/acme/verify.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/api/__init__.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/api/credentials.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/api/errors.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/dns/__init__.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/dns/errors.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/dns/names.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/dns/zones.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/src/devnomads/py.typed +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/conftest.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_acme_challenges.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_acme_client.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_acme_keys.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_client.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_credentials.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_dns.py +0 -0
- {devnomads-0.1.2 → devnomads-0.2.0}/tests/test_names.py +0 -0
devnomads-0.2.0/Makefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.PHONY: spec generate build check test
|
|
2
|
+
|
|
3
|
+
# refresh the committed OpenAPI snapshot from the live API
|
|
4
|
+
spec:
|
|
5
|
+
curl -sf "https://api.devnomads.nl/docs?api-docs.json" -o openapi.json
|
|
6
|
+
|
|
7
|
+
# regenerate src/devnomads/api/_services.py from openapi.json
|
|
8
|
+
generate:
|
|
9
|
+
uv run python tools/generate.py
|
|
10
|
+
uv run black -q .
|
|
11
|
+
uv run isort -q .
|
|
12
|
+
|
|
13
|
+
# full build: refresh spec, regenerate, verify
|
|
14
|
+
build: spec generate check
|
|
15
|
+
|
|
16
|
+
check:
|
|
17
|
+
uv run python tools/generate.py --check
|
|
18
|
+
uv run black --check .
|
|
19
|
+
uv run isort --check-only .
|
|
20
|
+
uv run flake8 src tests tools
|
|
21
|
+
uv run bandit -q -r src
|
|
22
|
+
uv run semgrep scan --quiet --error --config p/python src
|
|
23
|
+
uv run mypy src
|
|
24
|
+
uv run pytest -q
|
|
25
|
+
|
|
26
|
+
test:
|
|
27
|
+
uv run pytest -v
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devnomads
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python client for the DevNomads API (transport, DNS, ACME)
|
|
5
5
|
Project-URL: Homepage, https://devnomads.nl
|
|
6
6
|
Author-email: Loek Geleijn <support@devnomads.nl>
|
|
@@ -18,8 +18,8 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
# devnomads
|
|
19
19
|
|
|
20
20
|
Python client library for the [DevNomads](https://devnomads.nl) API:
|
|
21
|
-
HTTP transport,
|
|
22
|
-
issuance.
|
|
21
|
+
HTTP transport, a generated resource SDK covering the whole API, DNS
|
|
22
|
+
zone/record management, and ACME certificate issuance.
|
|
23
23
|
|
|
24
24
|
It is the shared foundation of the DevNomads command-line tools - `dnctl`
|
|
25
25
|
and `dnsync` - exposed so you can build against the same primitives
|
|
@@ -33,6 +33,7 @@ directly instead of reimplementing them.
|
|
|
33
33
|
- [Architecture](#architecture)
|
|
34
34
|
- [Authentication](#authentication)
|
|
35
35
|
- [Transport](#transport)
|
|
36
|
+
- [Services](#services)
|
|
36
37
|
- [DNS](#dns)
|
|
37
38
|
- [Certificates](#certificates)
|
|
38
39
|
- [Errors](#errors)
|
|
@@ -45,6 +46,8 @@ directly instead of reimplementing them.
|
|
|
45
46
|
|
|
46
47
|
- Authenticated HTTP transport with retries (`Retry-After` aware) and
|
|
47
48
|
Laravel resource-envelope unwrapping.
|
|
49
|
+
- A generated resource SDK covering the whole API as client
|
|
50
|
+
attributes, e.g. `client.servers.list()`.
|
|
48
51
|
- DNS zone discovery (longest-suffix matching), merge-aware TXT updates,
|
|
49
52
|
and generic record management.
|
|
50
53
|
- ACME certificate issuance over DNS-01 and HTTP-01 - including
|
|
@@ -85,13 +88,17 @@ The package is split into three layers by dependency weight. Import only
|
|
|
85
88
|
what you need.
|
|
86
89
|
|
|
87
90
|
```text
|
|
88
|
-
devnomads.api transport
|
|
91
|
+
devnomads.api transport + generated resource SDK (httpx)
|
|
89
92
|
▲
|
|
90
93
|
devnomads.dns zones, records, TXT/ACME challenges (httpx)
|
|
91
94
|
▲
|
|
92
95
|
devnomads.acme ACME issuance: DNS-01 + HTTP-01 (+ acme extra deps)
|
|
93
96
|
```
|
|
94
97
|
|
|
98
|
+
`devnomads.api` carries both the low-level `Client` transport and a
|
|
99
|
+
generated SDK that maps the full API onto client attributes (see
|
|
100
|
+
[Services](#services)).
|
|
101
|
+
|
|
95
102
|
The DevNomads DNS API is a PowerDNS proxy scoped to the zones a key may
|
|
96
103
|
access, so zone ids and record names are absolute (trailing dot) and TXT
|
|
97
104
|
content is stored quoted; the library handles these conventions for you.
|
|
@@ -182,6 +189,55 @@ JSON and:
|
|
|
182
189
|
resource reads as absence;
|
|
183
190
|
- raises `AuthError` on `401`/`403` and `ApiError` on any other `4xx`.
|
|
184
191
|
|
|
192
|
+
## Services
|
|
193
|
+
|
|
194
|
+
Beyond the curated DNS and ACME helpers, the `Client` exposes the rest
|
|
195
|
+
of the API through a generated, resource-attribute SDK. Each top-level
|
|
196
|
+
resource is an attribute on the client; nested resources chain as
|
|
197
|
+
further attributes; operations are methods.
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from devnomads.api import Client
|
|
201
|
+
|
|
202
|
+
with Client.from_environment() as client:
|
|
203
|
+
client.servers.list()
|
|
204
|
+
client.servers.show(server_id)
|
|
205
|
+
client.databases.clusters.list()
|
|
206
|
+
client.databases.create(body={"name": "blog"})
|
|
207
|
+
client.emails.mailboxes.list(service_id)
|
|
208
|
+
client.emails.mailboxes.create(service_id, body={...})
|
|
209
|
+
client.proxies.up(service_id)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The top-level resources are `servers`, `databases`, `emails`, `spams`,
|
|
213
|
+
`containers`, `proxies`, `sites`, `buckets`, `domains`, `forwards`,
|
|
214
|
+
`searches`, `apps`, and `handles`. (DNS stays the curated
|
|
215
|
+
`devnomads.dns` interface and is not generated here.)
|
|
216
|
+
|
|
217
|
+
### Conventions
|
|
218
|
+
|
|
219
|
+
- **Path parameters** are positional, in URL order, snake-cased from
|
|
220
|
+
the path template - e.g. `client.emails.mailboxes.show(service_id,
|
|
221
|
+
emailaddress)`.
|
|
222
|
+
- **Query parameters** go through `params=` as a dict.
|
|
223
|
+
- **Request bodies** (POST/PUT/PATCH) go through `body=`, sent as JSON.
|
|
224
|
+
- **Method names** map the API action: `index` -> `list`, `store`/
|
|
225
|
+
`create` -> `create`, `show`, `update`, `destroy`/`delete` ->
|
|
226
|
+
`delete`, `patch`; any other action (`state`, `deploy`, `logs`, `up`,
|
|
227
|
+
`down`, `enable`, ...) passes through snake-cased.
|
|
228
|
+
|
|
229
|
+
Returns are the parsed JSON payload (envelope already unwrapped). The
|
|
230
|
+
OpenAPI spec ships no component schemas, so there are no typed response
|
|
231
|
+
models - methods return `Any`. The SDK is generated from the committed
|
|
232
|
+
`openapi.json`; see [Development](#development).
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
mailbox = client.emails.mailboxes.create(
|
|
236
|
+
service_id, body={"address": "hi@example.com"}
|
|
237
|
+
)
|
|
238
|
+
aliases = client.emails.aliases.list(service_id, params={"page": 2})
|
|
239
|
+
```
|
|
240
|
+
|
|
185
241
|
## DNS
|
|
186
242
|
|
|
187
243
|
`Dns` wraps a `Client` with zone-scoped operations. Zone discovery lists
|
|
@@ -459,6 +515,16 @@ uv run pytest
|
|
|
459
515
|
uv run black . && uv run flake8 src tests
|
|
460
516
|
```
|
|
461
517
|
|
|
518
|
+
The `Services` SDK in `src/devnomads/api/_services.py` is generated from
|
|
519
|
+
the committed `openapi.json` snapshot. Regenerate it after refreshing
|
|
520
|
+
the spec; CI fails if the checked-in file is stale.
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
make spec # refresh openapi.json from the live API
|
|
524
|
+
make generate # rewrite _services.py from openapi.json
|
|
525
|
+
make check # lint + type + test + generate --check
|
|
526
|
+
```
|
|
527
|
+
|
|
462
528
|
## License
|
|
463
529
|
|
|
464
530
|
MIT
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# devnomads
|
|
2
2
|
|
|
3
3
|
Python client library for the [DevNomads](https://devnomads.nl) API:
|
|
4
|
-
HTTP transport,
|
|
5
|
-
issuance.
|
|
4
|
+
HTTP transport, a generated resource SDK covering the whole API, DNS
|
|
5
|
+
zone/record management, and ACME certificate issuance.
|
|
6
6
|
|
|
7
7
|
It is the shared foundation of the DevNomads command-line tools - `dnctl`
|
|
8
8
|
and `dnsync` - exposed so you can build against the same primitives
|
|
@@ -16,6 +16,7 @@ directly instead of reimplementing them.
|
|
|
16
16
|
- [Architecture](#architecture)
|
|
17
17
|
- [Authentication](#authentication)
|
|
18
18
|
- [Transport](#transport)
|
|
19
|
+
- [Services](#services)
|
|
19
20
|
- [DNS](#dns)
|
|
20
21
|
- [Certificates](#certificates)
|
|
21
22
|
- [Errors](#errors)
|
|
@@ -28,6 +29,8 @@ directly instead of reimplementing them.
|
|
|
28
29
|
|
|
29
30
|
- Authenticated HTTP transport with retries (`Retry-After` aware) and
|
|
30
31
|
Laravel resource-envelope unwrapping.
|
|
32
|
+
- A generated resource SDK covering the whole API as client
|
|
33
|
+
attributes, e.g. `client.servers.list()`.
|
|
31
34
|
- DNS zone discovery (longest-suffix matching), merge-aware TXT updates,
|
|
32
35
|
and generic record management.
|
|
33
36
|
- ACME certificate issuance over DNS-01 and HTTP-01 - including
|
|
@@ -68,13 +71,17 @@ The package is split into three layers by dependency weight. Import only
|
|
|
68
71
|
what you need.
|
|
69
72
|
|
|
70
73
|
```text
|
|
71
|
-
devnomads.api transport
|
|
74
|
+
devnomads.api transport + generated resource SDK (httpx)
|
|
72
75
|
▲
|
|
73
76
|
devnomads.dns zones, records, TXT/ACME challenges (httpx)
|
|
74
77
|
▲
|
|
75
78
|
devnomads.acme ACME issuance: DNS-01 + HTTP-01 (+ acme extra deps)
|
|
76
79
|
```
|
|
77
80
|
|
|
81
|
+
`devnomads.api` carries both the low-level `Client` transport and a
|
|
82
|
+
generated SDK that maps the full API onto client attributes (see
|
|
83
|
+
[Services](#services)).
|
|
84
|
+
|
|
78
85
|
The DevNomads DNS API is a PowerDNS proxy scoped to the zones a key may
|
|
79
86
|
access, so zone ids and record names are absolute (trailing dot) and TXT
|
|
80
87
|
content is stored quoted; the library handles these conventions for you.
|
|
@@ -165,6 +172,55 @@ JSON and:
|
|
|
165
172
|
resource reads as absence;
|
|
166
173
|
- raises `AuthError` on `401`/`403` and `ApiError` on any other `4xx`.
|
|
167
174
|
|
|
175
|
+
## Services
|
|
176
|
+
|
|
177
|
+
Beyond the curated DNS and ACME helpers, the `Client` exposes the rest
|
|
178
|
+
of the API through a generated, resource-attribute SDK. Each top-level
|
|
179
|
+
resource is an attribute on the client; nested resources chain as
|
|
180
|
+
further attributes; operations are methods.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from devnomads.api import Client
|
|
184
|
+
|
|
185
|
+
with Client.from_environment() as client:
|
|
186
|
+
client.servers.list()
|
|
187
|
+
client.servers.show(server_id)
|
|
188
|
+
client.databases.clusters.list()
|
|
189
|
+
client.databases.create(body={"name": "blog"})
|
|
190
|
+
client.emails.mailboxes.list(service_id)
|
|
191
|
+
client.emails.mailboxes.create(service_id, body={...})
|
|
192
|
+
client.proxies.up(service_id)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The top-level resources are `servers`, `databases`, `emails`, `spams`,
|
|
196
|
+
`containers`, `proxies`, `sites`, `buckets`, `domains`, `forwards`,
|
|
197
|
+
`searches`, `apps`, and `handles`. (DNS stays the curated
|
|
198
|
+
`devnomads.dns` interface and is not generated here.)
|
|
199
|
+
|
|
200
|
+
### Conventions
|
|
201
|
+
|
|
202
|
+
- **Path parameters** are positional, in URL order, snake-cased from
|
|
203
|
+
the path template - e.g. `client.emails.mailboxes.show(service_id,
|
|
204
|
+
emailaddress)`.
|
|
205
|
+
- **Query parameters** go through `params=` as a dict.
|
|
206
|
+
- **Request bodies** (POST/PUT/PATCH) go through `body=`, sent as JSON.
|
|
207
|
+
- **Method names** map the API action: `index` -> `list`, `store`/
|
|
208
|
+
`create` -> `create`, `show`, `update`, `destroy`/`delete` ->
|
|
209
|
+
`delete`, `patch`; any other action (`state`, `deploy`, `logs`, `up`,
|
|
210
|
+
`down`, `enable`, ...) passes through snake-cased.
|
|
211
|
+
|
|
212
|
+
Returns are the parsed JSON payload (envelope already unwrapped). The
|
|
213
|
+
OpenAPI spec ships no component schemas, so there are no typed response
|
|
214
|
+
models - methods return `Any`. The SDK is generated from the committed
|
|
215
|
+
`openapi.json`; see [Development](#development).
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
mailbox = client.emails.mailboxes.create(
|
|
219
|
+
service_id, body={"address": "hi@example.com"}
|
|
220
|
+
)
|
|
221
|
+
aliases = client.emails.aliases.list(service_id, params={"page": 2})
|
|
222
|
+
```
|
|
223
|
+
|
|
168
224
|
## DNS
|
|
169
225
|
|
|
170
226
|
`Dns` wraps a `Client` with zone-scoped operations. Zone discovery lists
|
|
@@ -442,6 +498,16 @@ uv run pytest
|
|
|
442
498
|
uv run black . && uv run flake8 src tests
|
|
443
499
|
```
|
|
444
500
|
|
|
501
|
+
The `Services` SDK in `src/devnomads/api/_services.py` is generated from
|
|
502
|
+
the committed `openapi.json` snapshot. Regenerate it after refreshing
|
|
503
|
+
the spec; CI fails if the checked-in file is stale.
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
make spec # refresh openapi.json from the live API
|
|
507
|
+
make generate # rewrite _services.py from openapi.json
|
|
508
|
+
make check # lint + type + test + generate --check
|
|
509
|
+
```
|
|
510
|
+
|
|
445
511
|
## License
|
|
446
512
|
|
|
447
513
|
MIT
|