Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# LightAPI v2 — Agent Context
# Branch: 1-v2-full-refactor
# Last updated: 2026-03-05
# SPEC: specs/1-v2-full-refactor/spec.md
# PLAN: specs/1-v2-full-refactor/plan.md

## Active Feature
Full refactor of LightAPI to v2. RestEndpoint is simultaneously the SQLAlchemy model and
Pydantic schema. No models.py, schemas.py, Column(), or @validates in user code.

## Tech Stack
- Python 3.10+
- SQLAlchemy 2.0 — imperative mapping (registry.map_imperatively), Select-based queries
- Pydantic v2 — Field(), create_model(), model_validate(), model_dump()
- Starlette — Request, JSONResponse, Response, Router, Route
- Uvicorn — ASGI server
- PyJWT — JWT auth (JWTAuthentication is FROZEN — do not modify)
- Redis — caching via frozen RedisCache/cache_manager
- PyYAML — YAML config loading
- pytest + httpx + SQLite — testing only

## Module Map
| File | Role |
|------|------|
| lightapi/exceptions.py | ConfigurationError, SerializationError |
| lightapi/methods.py | HttpMethod.GET/POST/PUT/PATCH/DELETE markers |
| lightapi/config.py | Authentication, Filtering, Pagination, Serializer, Cache |
| lightapi/fields.py | lightapi.Field() wrapper (strips foreign_key/unique/index/exclude) |
| lightapi/schema.py | SchemaFactory, _row_to_dict, _apply_fields, resolve_fields |
| lightapi/rest.py | RestEndpointMeta, RestEndpoint auto-CRUD |
| lightapi/auth.py | AllowAny, IsAuthenticated, IsAdminUser (JWTAuthentication FROZEN) |
| lightapi/filters.py | FilterBackend, SearchFilter, OrderingFilter |
| lightapi/pagination.py | PageNumberPaginator, CursorPaginator |
| lightapi/middleware.py | Middleware base |
| lightapi/__init__.py | LightApi, @app.route, @app.middleware, YAML, select shim |

## FROZEN — DO NOT MODIFY
- lightapi/auth.py: JWTAuthentication, BaseAuthentication (class bodies)
- lightapi/cache.py: RedisCache, BaseCache
- lightapi/swagger.py: SwaggerGenerator
- lightapi/lightapi.py: preserved as legacy until __init__.py rewrite absorbs it
- .github/workflows/
- pyproject.toml metadata fields (name, version, description, authors, etc.)

## Key Invariants (enforce in all generated code)
1. RestEndpoint IS the SQLAlchemy model AND the Pydantic schema.
2. Fields = annotated class attrs with Field(). Column() never appears in user code.
3. queryset is the only name for static and dynamic query definitions.
4. All validation via Pydantic Field(). @validates must not exist.
5. Every DB row passes through _row_to_dict → _apply_fields → model_validate → model_dump.
6. IsAdminUser checks payload["is_admin"] == True.
7. version (int, default 1) is auto-injected; PUT/PATCH require it; 409 on mismatch.
8. GET list always returns 200 with {"results": []} on empty; never 404.
9. DB errors propagate to Starlette's default 500 handler.

## Auto-Injected Columns (MUST NOT be redeclared)
id, created_at, updated_at, version

## Type Map (annotation → SQLAlchemy column)
str → String, Optional[str] → String nullable
int → Integer, Optional[int] → Integer nullable
float → Float, Optional[float] → Float nullable
bool → Boolean, Optional[bool] → Boolean nullable
datetime → DateTime, Optional[datetime] → DateTime nullable
Decimal → Numeric(scale=N)
UUID → Uuid

## Field() Custom Kwargs (stripped before Pydantic sees them)
foreign_key, unique, index, exclude, decimal_places
Stored in json_schema_extra; read by RestEndpointMeta.

## Error Responses
404: {"detail": "not found"}
409: {"detail": "version conflict"}
422: {"detail": [{loc, msg, type}, ...]} ← Pydantic v2 format
403: {"detail": "not allowed"}
405: {"detail": "method not allowed"} + Allow header

## Test Conventions (from constitution)
- test_<what>_<condition>_<expected> naming
- Arrange / Act / Assert
- sqlite:///:memory: for all DB fixtures
- Mock only at I/O boundary (DB session, Redis client)
- No external services

## Constitution Gates (must pass before merge)
1. ruff check
2. ruff format --check
3. mypy --strict
4. pytest -x (≥90% coverage on lightapi/)
4 changes: 2 additions & 2 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand All @@ -35,7 +35,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[test,dev]
pip install -e ".[dev,async]"

- name: Run tests
run: |
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/test-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand All @@ -30,10 +30,10 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[test,dev]
pip install -e ".[dev,async]"

- name: Run linting (Python 3.11 only)
if: matrix.python-version == '3.11'
- name: Run linting (Python 3.12 only)
if: matrix.python-version == '3.12'
run: |
# Install linting tools
pip install black isort flake8 mypy
Expand All @@ -59,13 +59,13 @@ jobs:
pytest tests/ -v --tb=short

- name: Test package build
if: matrix.python-version == '3.11'
if: matrix.python-version == '3.12'
run: |
pip install build
python -m build

- name: Check package
if: matrix.python-version == '3.11'
if: matrix.python-version == '3.12'
run: |
pip install twine
twine check dist/*
Expand Down
2 changes: 2 additions & 0 deletions LIGHTAPI_VALIDATION_REPORT.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> **Note:** This document describes the v1 implementation. The v2 YAML configuration format uses the `endpoints:` key and class references. See [README.md](README.md) for details.

# LightAPI Installation, Testing, and Validation Report

## Executive Summary
Expand Down
Loading
Loading