rtls-sdk 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.
- rtls_sdk-0.2.0/.gitignore +16 -0
- rtls_sdk-0.2.0/CHANGELOG.md +242 -0
- rtls_sdk-0.2.0/LICENSE +21 -0
- rtls_sdk-0.2.0/PKG-INFO +141 -0
- rtls_sdk-0.2.0/README.md +105 -0
- rtls_sdk-0.2.0/prompts/README.md +126 -0
- rtls_sdk-0.2.0/pyproject.toml +92 -0
- rtls_sdk-0.2.0/src/rtls_sdk/__init__.py +123 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_auth.py +266 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_client.py +419 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_envelope.py +74 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_http.py +145 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_logging.py +143 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_pagination.py +235 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_query.py +114 -0
- rtls_sdk-0.2.0/src/rtls_sdk/_time.py +84 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/__init__.py +19 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/auth.py +100 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/context.py +159 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/groups.py +126 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/nodes.py +176 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/reports.py +339 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/system.py +43 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/tags.py +404 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/users.py +143 -0
- rtls_sdk-0.2.0/src/rtls_sdk/compounds/zones.py +203 -0
- rtls_sdk-0.2.0/src/rtls_sdk/errors.py +238 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/__init__.py +73 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/_base.py +46 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/alarm.py +23 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/anchor.py +25 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/area.py +28 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/association.py +48 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/bulk.py +80 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/company.py +32 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/csv_blob.py +40 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/floorplan.py +42 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/group.py +20 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/heatmap.py +37 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/import_result.py +42 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/node.py +28 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/notification.py +38 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/position.py +66 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/project.py +26 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/pws.py +38 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/report.py +34 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/session_context.py +81 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/site.py +31 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/subscriber.py +68 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/system.py +97 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/system_health.py +36 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/tag.py +48 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/tag_template.py +24 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/user.py +121 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/zone.py +27 -0
- rtls_sdk-0.2.0/src/rtls_sdk/models/zone_event.py +21 -0
- rtls_sdk-0.2.0/src/rtls_sdk/py.typed +0 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/__init__.py +49 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/_base.py +63 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/alarms.py +108 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/anchors.py +147 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/areas.py +78 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/associations.py +157 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/auth.py +63 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/companies.py +79 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/context.py +50 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/events.py +149 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/floorplans.py +283 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/groups.py +99 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/logger.py +40 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/messaging.py +51 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/nodes.py +157 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/notifications.py +55 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/projects.py +67 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/reports.py +180 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/sites.py +115 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/subscribers.py +110 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/system.py +125 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/tags.py +370 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/users.py +275 -0
- rtls_sdk-0.2.0/src/rtls_sdk/resources/zones.py +199 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the RTLS SDK will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.2.0] — 2026-05-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
#### Places CRUD (M9)
|
|
15
|
+
|
|
16
|
+
- `client.sites.create / update / delete` — full lifecycle for the
|
|
17
|
+
wire `locations` resource. `update` uses a conservative `name` /
|
|
18
|
+
`shift` allowlist mirroring the JS reference client; `create`
|
|
19
|
+
accepts the wider server payload via `**fields`. Server-side
|
|
20
|
+
cascade on `delete` (areas / floorplans / anchors / zones go too).
|
|
21
|
+
- `client.areas.create / update / delete` — wire `sublocations`
|
|
22
|
+
resource. `create` requires `name` + `location_uid`; `update`
|
|
23
|
+
passes arbitrary placement fields (`position_x`, `position_y`,
|
|
24
|
+
`width`, `height`, `rotation`) through. Server-side cascade.
|
|
25
|
+
- `client.anchors.create / update / delete` — wire `win_anchors`.
|
|
26
|
+
`create` enforces `name`, `position`, `type` (`ANCHOR` or
|
|
27
|
+
`BRIDGE`, validated locally); optional `locked`, `acc_config`,
|
|
28
|
+
`bt_config`, `state`, `device_config` ride in `**fields`.
|
|
29
|
+
- `normalize_mac()` helper — accepts six input shapes (bare 12-hex
|
|
30
|
+
upper / lower, `:` or `-` separated) and emits the canonical
|
|
31
|
+
bare-12-hex-lowercase the server's `mac_regex` requires. Wired
|
|
32
|
+
through `nodes.create / validate / assign_raw / release_raw /
|
|
33
|
+
release / import_` and `associations.create / bulk_create`.
|
|
34
|
+
- Live fixtures `writable_site_uid` / `writable_area_uid` for
|
|
35
|
+
destructive live tests against the shared test server, with
|
|
36
|
+
`sdk-test-<uuid4>` prefixes and `yield`-style cleanup.
|
|
37
|
+
|
|
38
|
+
#### Floorplans + image lifecycle (M10)
|
|
39
|
+
|
|
40
|
+
- `client.floorplans.create` — base64-in-JSON image upload. Image
|
|
41
|
+
is required; pass `image_bytes` + `original_filename` OR
|
|
42
|
+
`image_path` (either-or, enforced with `ValueError` before any
|
|
43
|
+
HTTP call).
|
|
44
|
+
- `client.floorplans.update` — diff-driven; replaces the stored
|
|
45
|
+
image only when `image_bytes` / `image_path` is provided.
|
|
46
|
+
- `client.floorplans.delete` — removes the row and the stored
|
|
47
|
+
image.
|
|
48
|
+
- `client.floorplans.download_image` — 180-second read timeout
|
|
49
|
+
(overrides the default 30 s) for multi-MB PDFs; optional
|
|
50
|
+
`stream_to=<file>` for incremental writes.
|
|
51
|
+
- Server re-encodes PNGs on storage — documented in the entity
|
|
52
|
+
page; tests compare IHDR dimensions, not byte content.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
#### Schema audit (M12)
|
|
57
|
+
|
|
58
|
+
- **`tags.create` no longer accepts `mac_address`.** The server's
|
|
59
|
+
Joi `createTrackObject` schema has no `mac_address` field; the
|
|
60
|
+
default `stripUnknown` silently dropped it, so the call appeared
|
|
61
|
+
to succeed but the MAC never bound. Callers who want hardware
|
|
62
|
+
binding now go through `nodes.create` + `associations.create`
|
|
63
|
+
(the migration guide and `docs/entities/tags.md` document the
|
|
64
|
+
three-call replacement).
|
|
65
|
+
- **Canonical MAC format is bare 12-hex lowercase.** The server's
|
|
66
|
+
`mac_regex` rejects any separator; previous SDK responses /
|
|
67
|
+
examples that showed colon-separated MACs
|
|
68
|
+
(`AA:BB:CC:DD:EE:FF`) have been corrected across docs and tests.
|
|
69
|
+
`normalize_mac()` (introduced in M9) is now applied at every MAC
|
|
70
|
+
entry-point as a precondition, not a convenience.
|
|
71
|
+
- Regression gate: a live MAC round-trip test exercises the
|
|
72
|
+
create → list → release cycle to prove the wire format stays
|
|
73
|
+
canonical on both sides.
|
|
74
|
+
|
|
75
|
+
#### Wire-shape mismatches caught by the M14 full-stack live test
|
|
76
|
+
|
|
77
|
+
- **`companies.create` now includes a `role` on the seeded admin user**
|
|
78
|
+
(defaults to `"company_admin"`; override via `admin_role=`). The Joi
|
|
79
|
+
`companyCreate.user.role` field is required; previously the SDK omitted
|
|
80
|
+
it and the server responded `"user.role" is required`.
|
|
81
|
+
- **`projects.create` / `projects.update` now wrap the body under
|
|
82
|
+
`{"project": {...}}`** to match the Joi `createProject` /
|
|
83
|
+
`updateProject` schemas. The previously-flat body was rejected with
|
|
84
|
+
`"project" is required`.
|
|
85
|
+
- **`associations.close` now sends `mac_address` / `obj_uid` instead of
|
|
86
|
+
the association's own `uid`.** The server's close handler reads only
|
|
87
|
+
those two keys from the body; passing `{"uid": "..."}` raised
|
|
88
|
+
`"No mac or trackable object uid specified."`. The signature now
|
|
89
|
+
accepts an `Association` object, an association uid (with an extra
|
|
90
|
+
`list()` lookup), or explicit `mac_address` / `trackable_uid` kwargs.
|
|
91
|
+
`compounds/nodes.release` updated to pass the `Association` object
|
|
92
|
+
directly.
|
|
93
|
+
|
|
94
|
+
Each of these has a matching unit-test regression in
|
|
95
|
+
`tests/unit/test_resources_write_m3.py`.
|
|
96
|
+
|
|
97
|
+
### Documentation
|
|
98
|
+
|
|
99
|
+
- Reorganized user docs around **entities** instead of workflows
|
|
100
|
+
(M11). Every entity (tags, sites, areas, …) now has a single page
|
|
101
|
+
covering methods, examples, model fields, and cross-references —
|
|
102
|
+
answering "what can I do with X?" in one click from the new
|
|
103
|
+
`docs/entities/` catalog.
|
|
104
|
+
- Deleted `docs/guides/reading-data.md`,
|
|
105
|
+
`docs/guides/writing-data.md`, `docs/guides/session-context.md` —
|
|
106
|
+
superseded by entity pages.
|
|
107
|
+
- Trimmed `docs/guides/compound-workflows.md` to a concept page that
|
|
108
|
+
points at entity pages for examples.
|
|
109
|
+
- Nav restructure: Entities is now the second section after Quickstart;
|
|
110
|
+
cross-cutting guides (error handling, scope switching, pagination, …)
|
|
111
|
+
follow.
|
|
112
|
+
- README rewritten to lead with the entity catalog; previous flat
|
|
113
|
+
link list (which pointed at the now-deleted reading-data /
|
|
114
|
+
writing-data / session-context guides) replaced with a grouped
|
|
115
|
+
Start-here / Cross-cutting / Advanced / Reference structure.
|
|
116
|
+
|
|
117
|
+
## [0.1.0] — initial release
|
|
118
|
+
|
|
119
|
+
The first release covers the milestones M1–M8 of the original design.
|
|
120
|
+
The SDK provides a sync surface for the RTLS REST API with built-in
|
|
121
|
+
session management, scope handling, and compound workflows that
|
|
122
|
+
collapse multi-call sagas behind a single Python method.
|
|
123
|
+
|
|
124
|
+
### Added
|
|
125
|
+
|
|
126
|
+
#### Core client (M1)
|
|
127
|
+
|
|
128
|
+
- `RtlsClient(username, password, base_url, ...)` — construct from
|
|
129
|
+
explicit credentials.
|
|
130
|
+
- `RtlsClient(token, base_url, ...)` — bring-your-own token mode.
|
|
131
|
+
- `RtlsClient.from_env()` — construct from `RTLS_USERNAME` /
|
|
132
|
+
`RTLS_PASSWORD` / `RTLS_BASE_URL` (+ optional scope vars).
|
|
133
|
+
- Lazy login: no network I/O at construction; the first method call
|
|
134
|
+
performs `POST /auth/log_in.json`.
|
|
135
|
+
- Transparent 401 re-authentication with `threading.Lock`-protected
|
|
136
|
+
refresh coalescing.
|
|
137
|
+
- Mandatory secret redaction filter for the `rtls_sdk` named logger
|
|
138
|
+
(no opt-out). Headers and body keys redacted.
|
|
139
|
+
- Typed exception hierarchy under `RtlsError`:
|
|
140
|
+
`AuthenticationError`, `PermissionDenied`, `NotFound`,
|
|
141
|
+
`Conflict`, `ValidationError`, `RateLimited`, `ServerError`,
|
|
142
|
+
`ConnectionError`.
|
|
143
|
+
|
|
144
|
+
#### Read surface (M2)
|
|
145
|
+
|
|
146
|
+
- `client.tags.list/get`, `client.sites.list/get`,
|
|
147
|
+
`client.areas.list/get`, `client.floorplans.list/get`,
|
|
148
|
+
`client.zones.list/get`, `client.groups.list/get`,
|
|
149
|
+
`client.users.list/get/me`, `client.nodes.list/get`,
|
|
150
|
+
`client.anchors.list/get`, `client.alarms.list/get`,
|
|
151
|
+
`client.events.list/get`, `client.notifications.list/get`,
|
|
152
|
+
`client.subscribers.list/get`, `client.projects.list/get`,
|
|
153
|
+
`client.companies.list/get`, `client.reports.list/get`.
|
|
154
|
+
- `client.auth.whoami()`, `client.auth.capabilities()`.
|
|
155
|
+
- `client.system.host/uptime/connections/version/beta_version/ws_host/helper_link/monitor_link`.
|
|
156
|
+
- `client.messaging.*`, `client.logger.*`.
|
|
157
|
+
- Typed pydantic v2 models for every returned resource.
|
|
158
|
+
|
|
159
|
+
#### Simple writes (M3)
|
|
160
|
+
|
|
161
|
+
- `create_raw` / `update_raw` / `delete_raw` for all writable
|
|
162
|
+
resources — 1:1 wrappers over the REST surface.
|
|
163
|
+
- `auth.change_password(old, new)` with token invalidation.
|
|
164
|
+
- `users.create_raw / update_raw / change_password_raw` and
|
|
165
|
+
related raw flows.
|
|
166
|
+
|
|
167
|
+
#### Trackable compounds (M4)
|
|
168
|
+
|
|
169
|
+
- `tags.create(name, ..., attached_zone=..., groups=...)` — POST + zone
|
|
170
|
+
attach + groups bind with rollback.
|
|
171
|
+
- `tags.update`, `tags.delete` (incl. bulk-list form returning
|
|
172
|
+
`BulkResult`).
|
|
173
|
+
- `tags.bulk_update(uids, changes)`.
|
|
174
|
+
- `zones.create/update/delete` with dynamic-binding management.
|
|
175
|
+
- `groups.create/update/delete` with membership cleanup.
|
|
176
|
+
- `associations.bulk_create` continue-on-error.
|
|
177
|
+
|
|
178
|
+
#### User/Node compounds (M5)
|
|
179
|
+
|
|
180
|
+
- `users.create(email, password, ..., project_role=...)` — user +
|
|
181
|
+
project-role binding compound.
|
|
182
|
+
- `users.update(uid, ..., project_role=...)` — with project_role diff
|
|
183
|
+
semantics (add/remove/replace).
|
|
184
|
+
- `nodes.release(node_uid, project_uid=...)` — cross-scope release
|
|
185
|
+
with association close and per-thread scope override.
|
|
186
|
+
- `system.health()` — aggregated host + uptime + connections snapshot
|
|
187
|
+
with `partial`/`errors` fan-out tracking.
|
|
188
|
+
|
|
189
|
+
#### Session context (M6)
|
|
190
|
+
|
|
191
|
+
- `client.context.load()` — the dashboard bootstrap. Fans out ~20
|
|
192
|
+
sub-resource reads into one `SessionContext` snapshot with
|
|
193
|
+
`partial` / `errors` partial-failure semantics.
|
|
194
|
+
- Cross-scope nodes fetch via internal per-thread scope override.
|
|
195
|
+
|
|
196
|
+
#### Reports + pagination (M7)
|
|
197
|
+
|
|
198
|
+
- `client.reports.heatmap(...)` — POST + poll until ready, with
|
|
199
|
+
`ReportFailed` / `ReportTimeout` outcomes.
|
|
200
|
+
- `client.reports.pws(...)` — aggregate PWS event report.
|
|
201
|
+
- `client.reports.zone_activity_csv(...)` — chunked CSV download
|
|
202
|
+
following `content-next-page`, with optional streaming and
|
|
203
|
+
duplicate-header dedup.
|
|
204
|
+
- `client.reports.alarms_csv(...)` — alarms CSV with optional
|
|
205
|
+
multi-line banner prefix.
|
|
206
|
+
- `client.events.iter_pws(...)` — lazy iterator surface for PWS
|
|
207
|
+
events; three-style paginator infrastructure
|
|
208
|
+
(`HeaderCursorPaginator`, `BodyUrlPaginator`,
|
|
209
|
+
`PageLimitPaginator`).
|
|
210
|
+
- New models: `Heatmap`, `HeatmapInput`, `PwsReport`, `PwsEvent`,
|
|
211
|
+
`CsvBlob`.
|
|
212
|
+
|
|
213
|
+
#### Polish + release prep (M8)
|
|
214
|
+
|
|
215
|
+
- `client.with_scope(project_uid=..., company_uid=...)` — returns a
|
|
216
|
+
copy of the client bound to a different scope, sharing the same
|
|
217
|
+
authenticated session and connection pool.
|
|
218
|
+
- `client.use_project(uid)` / `client.use_company(uid)` — in-place
|
|
219
|
+
scope rebind.
|
|
220
|
+
- `RtlsClient(http_client=...)` — caller-supplied `httpx.Client`
|
|
221
|
+
for proxies, custom TLS, or other transport tuning.
|
|
222
|
+
- Comprehensive `docs/` site built with `mkdocs-material`
|
|
223
|
+
(guides, advanced topics, API reference auto-generated via
|
|
224
|
+
`mkdocstrings-python`, migration guide).
|
|
225
|
+
- Security audit test (`tests/security/test_secret_leaks.py`)
|
|
226
|
+
walking the public surface to verify redaction.
|
|
227
|
+
- `LICENSE` (MIT). `CHANGELOG.md`. CI workflow.
|
|
228
|
+
|
|
229
|
+
### Compatibility
|
|
230
|
+
|
|
231
|
+
- Python **3.10**, **3.11**, **3.12**.
|
|
232
|
+
- Wheel ships `py.typed`. Inline type hints work out of the box with
|
|
233
|
+
`mypy --strict` and `pyright`.
|
|
234
|
+
|
|
235
|
+
### Migration
|
|
236
|
+
|
|
237
|
+
Coming from the JS reference client? Read the migration guide
|
|
238
|
+
(`docs/migration/from-js-client.md`) — a code-by-code translation of
|
|
239
|
+
every common JS idiom into the SDK's equivalent.
|
|
240
|
+
|
|
241
|
+
[0.2.0]: https://github.com/rpplabs/rtls-sdk/releases/tag/v0.2.0
|
|
242
|
+
[0.1.0]: https://github.com/rpplabs/rtls-sdk/releases/tag/v0.1.0
|
rtls_sdk-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RTLS SDK Maintainers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
rtls_sdk-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rtls-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for the RTLS REST API
|
|
5
|
+
Project-URL: Homepage, https://github.com/rpplabs/rtls-sdk
|
|
6
|
+
Project-URL: Documentation, https://github.com/rpplabs/rtls-sdk/tree/master/docs
|
|
7
|
+
Project-URL: Issues, https://github.com/rpplabs/rtls-sdk/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/rpplabs/rtls-sdk/blob/master/CHANGELOG.md
|
|
9
|
+
Author: RTLS SDK Maintainers
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: iot,rtls,sdk,tracking
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: httpx>=0.27
|
|
23
|
+
Requires-Dist: pydantic>=2.5
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-httpserver>=1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
29
|
+
Requires-Dist: python-dotenv>=1.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
32
|
+
Provides-Extra: docs
|
|
33
|
+
Requires-Dist: mkdocs-material>=9; extra == 'docs'
|
|
34
|
+
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# RTLS SDK
|
|
38
|
+
|
|
39
|
+
A Python SDK for the RTLS REST API. Three-step usage:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from rtls_sdk import RtlsClient
|
|
43
|
+
|
|
44
|
+
client = RtlsClient.from_env()
|
|
45
|
+
tags = client.tags.list()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That's it — token, refresh, scoping headers, 401 re-login are all
|
|
49
|
+
internal. The first method call performs a lazy login; subsequent
|
|
50
|
+
calls reuse the session.
|
|
51
|
+
|
|
52
|
+
## What you get
|
|
53
|
+
|
|
54
|
+
- **Compound workflows.** One Python call replaces the multi-call
|
|
55
|
+
REST sagas the JS reference client makes you script — e.g.
|
|
56
|
+
`tags.create(name="X", attached_zone="Z", groups=[...])` runs the
|
|
57
|
+
three-step create-and-attach with rollback on partial failure.
|
|
58
|
+
- **One exception hierarchy.** Everything under `RtlsError`:
|
|
59
|
+
`NotFound`, `ValidationError`, `RateLimited`, `ServerError`,
|
|
60
|
+
`PartialFailureError`, `ReportFailed` / `ReportTimeout`, …
|
|
61
|
+
- **Typed models.** Pydantic v2 under the hood; `mypy --strict` clean
|
|
62
|
+
out of the box; ships `py.typed`.
|
|
63
|
+
- **Mandatory redaction.** No password, token, refresh-token, cookie,
|
|
64
|
+
or `Authorization` header ever reaches a log handler. There is no
|
|
65
|
+
opt-out flag.
|
|
66
|
+
- **Sync-only for v1.** Async surface and WebSocket support are on the
|
|
67
|
+
v2 roadmap.
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install rtls-sdk
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Python 3.10+ supported. See [`docs/install.md`](docs/install.md) for
|
|
76
|
+
dev install and verification.
|
|
77
|
+
|
|
78
|
+
## Quickstart
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
export RTLS_USERNAME="qa@example.com"
|
|
82
|
+
export RTLS_PASSWORD="hunter2"
|
|
83
|
+
export RTLS_BASE_URL="https://rtls.example.com"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from rtls_sdk import RtlsClient
|
|
88
|
+
|
|
89
|
+
with RtlsClient.from_env() as client:
|
|
90
|
+
for tag in client.tags.list():
|
|
91
|
+
print(tag.uid, tag.name)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
For the full 5-minute walkthrough see
|
|
95
|
+
[`docs/quickstart.md`](docs/quickstart.md).
|
|
96
|
+
|
|
97
|
+
## Documentation
|
|
98
|
+
|
|
99
|
+
The full docs site (`mkdocs build`):
|
|
100
|
+
|
|
101
|
+
**Start here**
|
|
102
|
+
|
|
103
|
+
- **[Entity catalog](docs/entities/index.md)** — one page per entity
|
|
104
|
+
(tags, sites, areas, floorplans, anchors, zones, nodes, users,
|
|
105
|
+
reports, …) with methods, examples, and model fields. The fastest
|
|
106
|
+
path to "what can I do with X?".
|
|
107
|
+
- [Install](docs/install.md) · [Quickstart](docs/quickstart.md)
|
|
108
|
+
|
|
109
|
+
**Cross-cutting guides**
|
|
110
|
+
|
|
111
|
+
- [Compound workflows](docs/guides/compound-workflows.md) · [Reports](docs/guides/reports.md) · [Floorplans](docs/guides/floorplans.md)
|
|
112
|
+
- [Error handling](docs/guides/error-handling.md) · [Scope switching](docs/guides/scope-switching.md) · [Pagination](docs/guides/pagination.md)
|
|
113
|
+
- [Rate limiting](docs/guides/rate-limiting.md) · [Timestamps](docs/guides/timestamps.md) · [Credential rotation](docs/guides/credential-rotation.md)
|
|
114
|
+
|
|
115
|
+
**Advanced**
|
|
116
|
+
|
|
117
|
+
- [BYO token](docs/advanced/byo-token.md) · [Raw REST methods](docs/advanced/raw-rest-methods.md) · [Logging](docs/advanced/logging.md) · [HTTP client override](docs/advanced/http-client-override.md) · [Security](docs/advanced/security.md)
|
|
118
|
+
|
|
119
|
+
**Reference**
|
|
120
|
+
|
|
121
|
+
- [Migrating from the JS client](docs/migration/from-js-client.md)
|
|
122
|
+
- API reference — auto-generated from docstrings via mkdocstrings.
|
|
123
|
+
|
|
124
|
+
Build locally:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pip install -e ".[docs]"
|
|
128
|
+
mkdocs serve
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Status
|
|
132
|
+
|
|
133
|
+
v0.2.0 — adds M9 places CRUD (sites / areas / anchors), M10 floorplans
|
|
134
|
+
+ image lifecycle, M11 entity-oriented documentation reorganisation,
|
|
135
|
+
and M12 schema-audit fixes (canonical bare-12-hex MACs;
|
|
136
|
+
`tags.create` no longer accepts the silently-stripped `mac_address`
|
|
137
|
+
argument). See [`CHANGELOG.md`](CHANGELOG.md).
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT — see [`LICENSE`](LICENSE).
|
rtls_sdk-0.2.0/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# RTLS SDK
|
|
2
|
+
|
|
3
|
+
A Python SDK for the RTLS REST API. Three-step usage:
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from rtls_sdk import RtlsClient
|
|
7
|
+
|
|
8
|
+
client = RtlsClient.from_env()
|
|
9
|
+
tags = client.tags.list()
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
That's it — token, refresh, scoping headers, 401 re-login are all
|
|
13
|
+
internal. The first method call performs a lazy login; subsequent
|
|
14
|
+
calls reuse the session.
|
|
15
|
+
|
|
16
|
+
## What you get
|
|
17
|
+
|
|
18
|
+
- **Compound workflows.** One Python call replaces the multi-call
|
|
19
|
+
REST sagas the JS reference client makes you script — e.g.
|
|
20
|
+
`tags.create(name="X", attached_zone="Z", groups=[...])` runs the
|
|
21
|
+
three-step create-and-attach with rollback on partial failure.
|
|
22
|
+
- **One exception hierarchy.** Everything under `RtlsError`:
|
|
23
|
+
`NotFound`, `ValidationError`, `RateLimited`, `ServerError`,
|
|
24
|
+
`PartialFailureError`, `ReportFailed` / `ReportTimeout`, …
|
|
25
|
+
- **Typed models.** Pydantic v2 under the hood; `mypy --strict` clean
|
|
26
|
+
out of the box; ships `py.typed`.
|
|
27
|
+
- **Mandatory redaction.** No password, token, refresh-token, cookie,
|
|
28
|
+
or `Authorization` header ever reaches a log handler. There is no
|
|
29
|
+
opt-out flag.
|
|
30
|
+
- **Sync-only for v1.** Async surface and WebSocket support are on the
|
|
31
|
+
v2 roadmap.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install rtls-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Python 3.10+ supported. See [`docs/install.md`](docs/install.md) for
|
|
40
|
+
dev install and verification.
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
export RTLS_USERNAME="qa@example.com"
|
|
46
|
+
export RTLS_PASSWORD="hunter2"
|
|
47
|
+
export RTLS_BASE_URL="https://rtls.example.com"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from rtls_sdk import RtlsClient
|
|
52
|
+
|
|
53
|
+
with RtlsClient.from_env() as client:
|
|
54
|
+
for tag in client.tags.list():
|
|
55
|
+
print(tag.uid, tag.name)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For the full 5-minute walkthrough see
|
|
59
|
+
[`docs/quickstart.md`](docs/quickstart.md).
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
The full docs site (`mkdocs build`):
|
|
64
|
+
|
|
65
|
+
**Start here**
|
|
66
|
+
|
|
67
|
+
- **[Entity catalog](docs/entities/index.md)** — one page per entity
|
|
68
|
+
(tags, sites, areas, floorplans, anchors, zones, nodes, users,
|
|
69
|
+
reports, …) with methods, examples, and model fields. The fastest
|
|
70
|
+
path to "what can I do with X?".
|
|
71
|
+
- [Install](docs/install.md) · [Quickstart](docs/quickstart.md)
|
|
72
|
+
|
|
73
|
+
**Cross-cutting guides**
|
|
74
|
+
|
|
75
|
+
- [Compound workflows](docs/guides/compound-workflows.md) · [Reports](docs/guides/reports.md) · [Floorplans](docs/guides/floorplans.md)
|
|
76
|
+
- [Error handling](docs/guides/error-handling.md) · [Scope switching](docs/guides/scope-switching.md) · [Pagination](docs/guides/pagination.md)
|
|
77
|
+
- [Rate limiting](docs/guides/rate-limiting.md) · [Timestamps](docs/guides/timestamps.md) · [Credential rotation](docs/guides/credential-rotation.md)
|
|
78
|
+
|
|
79
|
+
**Advanced**
|
|
80
|
+
|
|
81
|
+
- [BYO token](docs/advanced/byo-token.md) · [Raw REST methods](docs/advanced/raw-rest-methods.md) · [Logging](docs/advanced/logging.md) · [HTTP client override](docs/advanced/http-client-override.md) · [Security](docs/advanced/security.md)
|
|
82
|
+
|
|
83
|
+
**Reference**
|
|
84
|
+
|
|
85
|
+
- [Migrating from the JS client](docs/migration/from-js-client.md)
|
|
86
|
+
- API reference — auto-generated from docstrings via mkdocstrings.
|
|
87
|
+
|
|
88
|
+
Build locally:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e ".[docs]"
|
|
92
|
+
mkdocs serve
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Status
|
|
96
|
+
|
|
97
|
+
v0.2.0 — adds M9 places CRUD (sites / areas / anchors), M10 floorplans
|
|
98
|
+
+ image lifecycle, M11 entity-oriented documentation reorganisation,
|
|
99
|
+
and M12 schema-audit fixes (canonical bare-12-hex MACs;
|
|
100
|
+
`tags.create` no longer accepts the silently-stripped `mac_address`
|
|
101
|
+
argument). See [`CHANGELOG.md`](CHANGELOG.md).
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# SDK implementation prompts
|
|
2
|
+
|
|
3
|
+
These prompts implement the design in `/Users/yzhbankov/rtls/rtls-sdk/DESIGN.md`. Run them sequentially. Each milestone leaves the SDK in a working, tested, lint-clean state. After milestone 8 the SDK is feature-complete for v1 and ready to publish.
|
|
4
|
+
|
|
5
|
+
| # | Prompt | Milestone | Delivers |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| 03 | `03-m1-foundation.md` | M1 | First end-to-end: `RtlsClient.from_env().tags.list()` works against a mocked server. Auth, errors, redaction, packaging, CI scaffolding. |
|
|
8
|
+
| 04 | `04-m2-read-surface.md` | M2 | All `.list()` / `.get()` methods for every resource the JS client reads. All read-side pydantic models. |
|
|
9
|
+
| 05 | `05-m3-simple-writes.md` | M3 | All `.create()` / `.update()` / `.delete()` as thin REST mirrors (also the escape hatches for compound methods). |
|
|
10
|
+
| 06 | `06-m4-trackable-compounds.md` | M4 | Compound workflows: `tags.create(attached_zone=..., groups=...)`, `tags.update`, `tags.delete` cascade, `tags.bulk_update`, dynamic-zone re-binding, `groups.add_tags` / `groups.delete` cascade. `PartialFailureError` wiring. |
|
|
11
|
+
| 07 | `07-m5-user-node-compounds.md` | M5 | Compound user/node workflows: `users.create` / `users.update` with project-role binding, `nodes.release`, `nodes.import_`, `auth.change_password` with re-login. |
|
|
12
|
+
| 08 | `08-m6-context-load.md` | M6 | The `client.context.load()` mega-method — one call hydrates 16+ entities. |
|
|
13
|
+
| 09 | `09-m7-reports.md` | M7 | Reports + pagination: `reports.heatmap` (polling), `reports.pws`, `reports.zone_activity_csv` (chunked), `events.iter_pws` / `events.pages` (three pagination styles unified). |
|
|
14
|
+
| 10 | `10-m8-polish-docs.md` | M8 | Polish, comprehensive docs site (mkdocs-material), API reference, CHANGELOG, release prep. v0.1.0 ships. |
|
|
15
|
+
| 11 | `11-m9-places-crud.md` | M9 | Site / area / anchor full CRUD — closes the v0.1 gap for the `desktop-site_plan` client. MAC normalization, server-cascade contract, new `writable_site_uid` / `writable_area_uid` live fixtures. |
|
|
16
|
+
| 12 | `12-m10-floorplans-images.md` | M10 | Floorplans full CRUD + image upload (base64-in-JSON) + image download (180 s timeout, streaming). Completes the place-entity gap. |
|
|
17
|
+
| 13 | `13-m11-entity-docs.md` | M11 | Docs-only: reorganize user documentation around **entities**. New `docs/entities/` tree (one page per entity + catalog index) replaces the workflow-oriented guides. Answers "what can I do with X?" on one page. |
|
|
18
|
+
| 14 | `14-m12-schema-audit-mac-fix.md` | M12 | Schema-correctness audit. Fix three real bugs caught by reading the server's Joi schemas directly: MAC format is bare 12-hex (not colon-separated); `tags.create` does not accept `mac_address` (server silently strips); add live MAC round-trip test as a regression gate. |
|
|
19
|
+
| 15 | `15-m13-final-audit.md` | M13 | **Check-only.** Full ship-readiness audit across code quality, documentation site, internal consistency, wire-shape correctness, deep doc-quality analysis (with persona-driven proposals), and release artifacts. Produces `AUDIT.md` at the repo root with a verdict (ready / not ready), itemised blockers, repo-wide markdown link validation, and a prioritised list of documentation improvement proposals. Does not modify code or docs — a separate milestone applies any accepted fixes. |
|
|
20
|
+
|
|
21
|
+
## Inputs each prompt reads
|
|
22
|
+
|
|
23
|
+
- `/Users/yzhbankov/rtls/rtls-sdk/DESIGN.md` — authoritative design.
|
|
24
|
+
- `/Users/yzhbankov/rtls/rtls-sdk/RESEARCH.md` — endpoint inventory, compound workflows, quirks.
|
|
25
|
+
- `/Users/yzhbankov/rtls/rtls-frontend/client/src/modules/api/` — reference JS client. When docs and client disagree, client wins.
|
|
26
|
+
- `/Users/yzhbankov/rtls/python-libraries/` — prior art for test scaffolding (`pytest-httpserver` handlers).
|
|
27
|
+
|
|
28
|
+
## Standing constraints (apply to every milestone)
|
|
29
|
+
|
|
30
|
+
1. **The three-step user mental model is sacred.** No idiomatic user-facing code touches tokens, refresh, or scoping headers. If a snippet in Section 1 of DESIGN.md would have to change to use what you built, you built the wrong thing.
|
|
31
|
+
2. **§8 of RESEARCH.md is binding.** Stack picks (`httpx`, `pydantic`, sync v1, `ruff`, `pytest`), credential UX, lazy login, 401 re-login, in-memory token only, mandatory secret redaction — all decided.
|
|
32
|
+
3. **Type hints everywhere.** `py.typed` marker shipped; `mypy --strict src/` passes (configured to permit `Any` in pydantic field validators only).
|
|
33
|
+
4. **Tests on every new method.** Unit (`respx`) for behaviour, integration (`pytest-httpserver`) for end-to-end call sequences in compounds.
|
|
34
|
+
5. **Docstrings on every public method, class, and module.** Numpy or Google style — pick one in M1 and keep it. Include a minimal usage example in each public method's docstring.
|
|
35
|
+
6. **Lint clean.** `ruff check` and `ruff format --check` both pass. CI fails on warnings.
|
|
36
|
+
7. **No backwards-compat carry-over.** Each milestone builds on the last with no shims, no deprecated-but-kept methods, no migration guides. If something is wrong, fix it cleanly.
|
|
37
|
+
|
|
38
|
+
## How to run a prompt
|
|
39
|
+
|
|
40
|
+
Open the prompt file. It assumes Claude is starting fresh with no memory of the previous prompt — but with access to the files the previous prompt left behind. The prompt is self-contained: it tells Claude what to read, what to build, and how to verify.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Live-server validation protocol (applies to every milestone)
|
|
45
|
+
|
|
46
|
+
A live RTLS test server is available. **Every milestone has a final "Live verification" gate**: the work isn't done until the milestone's behavior has been validated against the real server, not just against mocks. Mocks catch what we anticipated; the live server catches what we didn't.
|
|
47
|
+
|
|
48
|
+
### One-time credentials onboarding (do this at the start of M1)
|
|
49
|
+
|
|
50
|
+
The first prompt (M1) does this once. Subsequent prompts reuse what M1 set up. The agent must:
|
|
51
|
+
|
|
52
|
+
1. **Ask the user for** (via `AskUserQuestion`):
|
|
53
|
+
- `RTLS_LIVE_BASE_URL` — e.g. `https://rtls.test.example.com`
|
|
54
|
+
- `RTLS_LIVE_USERNAME` — superuser email
|
|
55
|
+
- `RTLS_LIVE_PASSWORD` — superuser password
|
|
56
|
+
- `RTLS_LIVE_PROJECT_UID` — a project uid the test user has access to (optional but used by M2+)
|
|
57
|
+
- `RTLS_LIVE_COMPANY_UID` — a company uid (optional; needed for company-scope tests in M5–M6)
|
|
58
|
+
|
|
59
|
+
2. **Write them to `.env.local`** at the repo root in dotenv format. **Do not commit this file.** M1's `.gitignore` must include `.env.local` (and `.env.*.local`) on its first line.
|
|
60
|
+
|
|
61
|
+
3. **Create `tests/live/conftest.py`** with this shape:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import os
|
|
65
|
+
import pytest
|
|
66
|
+
from dotenv import load_dotenv
|
|
67
|
+
|
|
68
|
+
load_dotenv(".env.local")
|
|
69
|
+
|
|
70
|
+
def _require(name: str) -> str:
|
|
71
|
+
val = os.getenv(name)
|
|
72
|
+
if not val:
|
|
73
|
+
pytest.skip(f"{name} not set in .env.local — skipping live tests")
|
|
74
|
+
return val
|
|
75
|
+
|
|
76
|
+
@pytest.fixture(scope="session")
|
|
77
|
+
def live_creds():
|
|
78
|
+
return {
|
|
79
|
+
"base_url": _require("RTLS_LIVE_BASE_URL"),
|
|
80
|
+
"username": _require("RTLS_LIVE_USERNAME"),
|
|
81
|
+
"password": _require("RTLS_LIVE_PASSWORD"),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@pytest.fixture(scope="session")
|
|
85
|
+
def live_project_uid() -> str:
|
|
86
|
+
return _require("RTLS_LIVE_PROJECT_UID")
|
|
87
|
+
|
|
88
|
+
@pytest.fixture
|
|
89
|
+
def live_client(live_creds, live_project_uid):
|
|
90
|
+
from rtls_sdk import RtlsClient
|
|
91
|
+
with RtlsClient(**live_creds, project_uid=live_project_uid) as client:
|
|
92
|
+
yield client
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
4. **Register the `live` pytest marker** in `pyproject.toml`:
|
|
96
|
+
|
|
97
|
+
```toml
|
|
98
|
+
[tool.pytest.ini_options]
|
|
99
|
+
markers = ["live: tests that hit a real RTLS server (require .env.local)"]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
And in each `tests/live/test_*.py` file, mark tests with `pytestmark = pytest.mark.live` at module top.
|
|
103
|
+
|
|
104
|
+
5. **Add `python-dotenv` to the dev extras** in `pyproject.toml` (no runtime dep).
|
|
105
|
+
|
|
106
|
+
### Default vs. live test runs
|
|
107
|
+
|
|
108
|
+
- `pytest` (no marker filter) runs unit + mocked integration tests only. Live tests are auto-skipped if `.env.local` is missing. CI runs this.
|
|
109
|
+
- `pytest -m live` runs the live tests. Agent runs this at the end of every milestone as the final verification gate.
|
|
110
|
+
- `pytest -m "not live"` is the explicit safe form for CI / sandboxes that must never reach the network.
|
|
111
|
+
|
|
112
|
+
### Cleanup discipline (binding from M3 onward)
|
|
113
|
+
|
|
114
|
+
Live tests that **create** server-side state MUST clean it up. Use `yield`-style fixtures or `try/finally`. The live server is shared infrastructure, not scratch space.
|
|
115
|
+
|
|
116
|
+
Naming convention: every test-created resource gets a deterministic `name` or `description` prefix `sdk-test-<uuid4>` so a periodic cleanup script can sweep stale resources if a test crashes hard. Add a `tests/live/_cleanup.py` helper that the M3 prompt sets up.
|
|
117
|
+
|
|
118
|
+
If a live test fails mid-flight, the cleanup fixture still runs (that's why we use `yield` fixtures, not `setup/teardown` returns). Tests must be idempotent: running them twice on a fresh database produces the same outcome as running them once.
|
|
119
|
+
|
|
120
|
+
### Credentials in logs
|
|
121
|
+
|
|
122
|
+
The redaction filter from M1 strips passwords/tokens from logs. Verify this is working with a live test before treating any subsequent live test output as safe to share. **Never paste raw test logs back to the user without re-checking they don't contain credentials.**
|
|
123
|
+
|
|
124
|
+
### What a milestone's "Live verification" gate covers
|
|
125
|
+
|
|
126
|
+
The standing rule: **the milestone's primary user-facing happy path must work against the real server, with cleanup.** Each prompt lists the specific live checks for that milestone. The expected pattern is small (3–8 live tests per milestone, each ~10 LoC), focused on shape and integration, not exhaustive coverage — unit tests still own coverage.
|