|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This repository contains **acai_aws**, a happy-path-first toolkit for AWS Lambda handlers. This guide distills the canonical documentation so another AI agent can navigate the codebase without digging through the docs. |
| 4 | + |
| 5 | +## Philosophy & Environment |
| 6 | +- Validate early, execute later. Every handler or event processor should assume inputs are clean because requirements and middleware already filtered the bad paths. |
| 7 | +- Python 3.8+ is the baseline. Install dependencies via `pipenv install --dev` and use `pipenv run` to invoke tooling so the virtualenv stays consistent. |
| 8 | +- Preferred workflow for any meaningful change: `pipenv run lint`, `pipenv run test`, `pipenv run coverage`, and—when request/response contracts change—`pipenv run generate` to refresh the OpenAPI spec. |
| 9 | + |
| 10 | +## API Gateway Architecture |
| 11 | +### Router Responsibilities |
| 12 | +- The router owns request dispatch, validation, middleware orchestration, optional auth, and error handling. Core options include: |
| 13 | + - `base_path` (required) to match the API Gateway stage. |
| 14 | + - `handlers` (required) pointing to the directory that mirrors routes. |
| 15 | + - `schema` plus `auto_validate`/`validate_response` to wire OpenAPI checks. |
| 16 | + - `cors` (default `True`), `timeout`, `output_error`, `verbose_logging`, `cache_mode` (`all`, `static-only`, `dynamic-only`), and `cache_size` (default `128`). |
| 17 | + - Middleware hooks: `before_all`, `after_all`, `with_auth`, `on_error`, `on_timeout`. |
| 18 | +- Always call `router.auto_load()` so directory traversal happens once during cold start. Dynamic imports are cached per the `cache_mode`/`cache_size` combination. |
| 19 | + |
| 20 | +### File-System Routing Rules |
| 21 | +- Directory names become path segments; `__init__.py` represents the index route for that segment. |
| 22 | +- Files prefixed with `_` declare a path parameter: `_user_id.py` responds to `/users/{user_id}`. Keep the placeholder name identical to the suffix (`user_id`) so `request.path_params` stays intuitive. |
| 23 | +- All HTTP verbs map to functions with lowercase names (`get`, `post`, etc.). Unsupported verbs simply don’t exist in the handler module. |
| 24 | + |
| 25 | +### Middleware Order |
| 26 | +1. `before_all` |
| 27 | +2. Route-level `before` hook |
| 28 | +3. Authentication via `with_auth` when `auth_required=True` |
| 29 | +4. Handler function |
| 30 | +5. Route-level `after` hook |
| 31 | +6. `after_all` |
| 32 | +7. Error/timeout hooks only if an exception bubbles up |
| 33 | + |
| 34 | +Maintain this order whenever touching router internals; tests expect it. |
| 35 | + |
| 36 | +## Handler Requirements & Validation |
| 37 | +- Use `@requirements(...)` from `acai_aws.apigateway.requirements` to describe what makes a request valid. Supported keys include: |
| 38 | + - `required_headers`, `available_headers` |
| 39 | + - `required_query`, `available_query` |
| 40 | + - `required_route` for dynamic segments (e.g., `grower/{grower_id}`) |
| 41 | + - `required_body` and `required_response` referencing OpenAPI components or inline JSON Schema dicts |
| 42 | + - `auth_required` to trigger `with_auth` |
| 43 | + - `request_class` to swap in a custom request wrapper |
| 44 | + - `before`/`after` functions scoped to a single endpoint |
| 45 | + - `timeout` overrides (seconds, handler runtime only) |
| 46 | + - `summary`, `deprecated`, and arbitrary custom fields for OpenAPI generation or middleware hints |
| 47 | +- All validation errors should flow through `response.set_error` so clients receive consistent payloads. |
| 48 | + |
| 49 | +## Request & Response Contracts |
| 50 | +- **Request** objects expose normalized HTTP metadata: `method`, `headers`, `query_params`, `path_params`, `route`, `path`, `cookies`, and `authorizer`. Body helpers auto-detect based on `Content-Type`: |
| 51 | + - `request.json`, `request.form`, `request.xml`, `request.graphql`, and `request.body` (auto-converts but falls back to raw data). |
| 52 | + - `request.raw` retains the unmodified payload. |
| 53 | + - `request.context` is the only mutable property—middleware can stash data here for later steps. |
| 54 | +- **Response** objects provide: |
| 55 | + - `response.code` (default 200) and `response.headers` (tuples merged into a dict). |
| 56 | + - `response.body` with automatic JSON serialization. |
| 57 | + - `response.raw` to bypass serialization when `after`/`after_all` need to mutate the payload. |
| 58 | + - `response.compress` to gzip the final body. |
| 59 | + - `response.set_error(key, message)` and `response.has_error` for structured error responses. |
| 60 | + |
| 61 | +## OpenAPI Generation |
| 62 | +- Run `python -m acai_aws.apigateway generate-openapi --handlers=<glob> --base=<base_path> --output=<dir> --format=json,yml --delete` when handler requirements change. This command scans decorated functions, syncs paths/methods, and optionally prunes stale routes (`--delete`). Keep handler metadata descriptive so the generated doc is meaningful. |
| 63 | + |
| 64 | +## Event Processors (Non-HTTP) |
| 65 | +Every AWS event module follows the same shape: import `requirements` from the service package, decorate a handler, and iterate over normalized records. |
| 66 | + |
| 67 | +| Service | Highlights | |
| 68 | +|---------|------------| |
| 69 | +| DynamoDB | Converts Dynamo JSON to native dicts, can filter by operations (`INSERT`, `MODIFY`, `REMOVE` or aliases), validates record bodies, supports `data_class` injection. | |
| 70 | +| S3 | Optional `get_object` fetches the object body. `data_type` controls parsing (`json`, `csv`, or raw). Operations filters focus on create/update/delete events. | |
| 71 | +| SQS/SNS | Normalizes message bodies to dicts, surfaces message attributes, and supports JSON Schema validation plus data classes. | |
| 72 | +| Kinesis/Firehose/MSK/MQ | Batch processors built on `BaseRecordsEvent`; can apply schema validation and data classes per record, keeping streaming semantics intact. | |
| 73 | +| DocumentDB | Tailored for change streams with access to `operationType`, full documents, and metadata. | |
| 74 | +| Generic | Simplifies console/CLI-triggered events with body parsing and schema validation. | |
| 75 | + |
| 76 | +Tips when editing event modules: |
| 77 | +- Keep constructor signatures consistent so `event.records` always returns iterable record objects. |
| 78 | +- Honor `operations` filters; when they’re misconfigured the event should no-op unless `raise_operation_error=True`. |
| 79 | +- Data classes receive a `record` instance. Keep their API stable to avoid breaking user code. |
| 80 | + |
| 81 | +## Logger & Observability |
| 82 | +- `acai_aws.common.logger` logs structured JSON by default. Switching `LOG_FORMAT=INLINE` helps during local dev while `LOG_FORMAT=JSON` keeps CloudWatch-friendly output. `LOG_LEVEL` gates log emission (`INFO`, `DEBUG`, `WARN`, `ERROR`). |
| 83 | +- The `@log` decorator wraps any function, optionally gating logs with a boolean `condition`. Maintain argument pass-through so debugging remains straightforward. |
| 84 | +- Error traces should include the stack plus the high-level message; tests assert the JSON keys stay consistent (`level`, `time`, `error_trace`, `log`). |
| 85 | + |
| 86 | +## Agent Checklist |
| 87 | +1. **Understand the route or event** you’re touching—confirm how the filesystem maps to the API or which event module processes the payload. |
| 88 | +2. **Update requirements first**, letting validation guardrails enforce behavior instead of branching in business logic. |
| 89 | +3. **Keep Request/Response contracts untouched** unless there’s a strong reason; these are relied on by every handler. |
| 90 | +4. **Mirror changes across services** when working inside shared abstractions like `BaseRecordsEvent`. |
| 91 | +5. **Write or update tests** under `tests/acai_aws/...` that mirror the source layout. |
| 92 | +6. **Run lint/tests/coverage/generate** (noted above) and capture any failures or skipped steps in your summary so humans can follow up. |
| 93 | +7. **Document surprises**: if AWS quirks require deviations from the standard flow, leave concise comments or docstrings explaining the reason. |
| 94 | + |
| 95 | +Following this guide keeps future agents aligned with Acai’s happy-path principles while minimizing regressions across API Gateway handlers and event processors alike. |
0 commit comments