|
| 1 | +# Agent guide |
| 2 | + |
| 3 | +Guidance for AI assistants working in this repository. |
| 4 | + |
| 5 | +## Setup |
| 6 | + |
| 7 | +```bash |
| 8 | +pip install -e ".[dev]" |
| 9 | +pytest tests/ -v |
| 10 | +``` |
| 11 | + |
| 12 | +No network access is needed for tests; every HTTP call is mocked with `unittest.mock.patch` on `requests.Session.get` / `requests.Session.post`. |
| 13 | + |
| 14 | +## Project structure |
| 15 | + |
| 16 | +``` |
| 17 | +datalastic/ |
| 18 | + __init__.py public exports (Client, models, exceptions) |
| 19 | + client.py Client + _get/_post/_handle + base URLs + stat() |
| 20 | + vessels.py VesselsResource (8 methods) |
| 21 | + ports.py PortsResource (find, get) |
| 22 | + routes.py RoutesResource (calculate) |
| 23 | + intel.py IntelResource (8 maritime_reports endpoints) |
| 24 | + reports.py ReportsResource (submit, get, list_all) |
| 25 | + models.py dict-backed data classes, each with .raw and __repr__ |
| 26 | + exceptions.py exception hierarchy |
| 27 | +tests/test_client.py |
| 28 | +``` |
| 29 | + |
| 30 | +## Key conventions |
| 31 | + |
| 32 | +- **Auth is a query parameter.** `api-key` is appended to params in `_get()` and |
| 33 | + injected into the JSON body in `_post()`. Never a header. |
| 34 | +- **Three base URLs.** `BASE_V0` (default), `BASE_EXT` (sea routes, SAT-E), |
| 35 | + `BASE_MR` (all `intel` endpoints). Pass `base=` to `_get()`. |
| 36 | +- **Date params are renamed.** Python `from_date` / `to_date` are sent as the |
| 37 | + `from` / `to` query keys. `vessel_type` in `vessels.find()` is sent as `type` |
| 38 | + to avoid shadowing the builtin. |
| 39 | +- **None filtering.** Always strip `None` values before building the request |
| 40 | + (`{k: v for k, v in params.items() if v is not None}`). |
| 41 | +- **Validate before HTTP.** Required-parameter checks raise `ValueError` |
| 42 | + before any request is made. |
| 43 | +- **Bulk uses repeated params.** `vessels.bulk()` builds a list of |
| 44 | + `(key, value)` tuples so identifiers are sent as `mmsi=x&mmsi=y`. |
| 45 | +- **Response unwrapping.** `_get()` / `_post()` unwrap the envelope and return |
| 46 | + `body["data"]` directly; callers use the returned value as the model data. |
| 47 | + List endpoints map each element into a model. |
| 48 | +- **Models never raise on missing fields.** They use `.get()` and keep the |
| 49 | + raw payload in `.raw`. |
| 50 | + |
| 51 | +## Exception hierarchy |
| 52 | + |
| 53 | +``` |
| 54 | +DatalasticError |
| 55 | +├── AuthenticationError 401 |
| 56 | +├── InsufficientCreditsError 402 |
| 57 | +├── NotFoundError 404 |
| 58 | +├── RateLimitError 429 |
| 59 | +└── APIError everything else; has .status_code |
| 60 | +``` |
| 61 | + |
| 62 | +Timeouts, connection errors, non-JSON bodies, and missing `data` keys all map |
| 63 | +to `APIError`. |
| 64 | + |
| 65 | +## Testing conventions |
| 66 | + |
| 67 | +- Build fake responses with the `make_response()` / `ok()` helpers in |
| 68 | + `tests/test_client.py`. |
| 69 | +- Every resource method has a happy-path test plus validation-error tests. |
| 70 | +- Assert the called URL prefix to confirm the correct base URL is used. |
| 71 | +- Assert query params / JSON body to confirm renaming and auth injection. |
| 72 | +- Keep tests offline — never hit the live API. |
0 commit comments