Skip to content

Commit 19b77c4

Browse files
authored
Merge pull request #671 from VariantEffect/release-2026.1.1
Release 2026.1.1
2 parents 9c65d44 + 48112fb commit 19b77c4

55 files changed

Lines changed: 5442 additions & 3960 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/instructions/ai-prompt-engineering-safety-best-practices.instructions.md

Lines changed: 0 additions & 867 deletions
This file was deleted.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
description: 'MaveDB API patterns — routers, authentication, authorization, endpoints'
3+
applyTo: 'src/mavedb/routers/**/*.py'
4+
---
5+
6+
# API Patterns for MaveDB
7+
8+
## Router Structure
9+
10+
All routers use:
11+
- `ROUTER_BASE_PREFIX = "/api/v1"` from `src/mavedb/routers/__init__.py`
12+
- `LoggedRoute` as the custom `route_class` for canonical request/response logging
13+
- Kebab-case URL paths: `/score-sets`, `/experiment-sets`
14+
15+
```python
16+
router = APIRouter(
17+
prefix="/api/v1/score-sets",
18+
tags=["score-sets"],
19+
route_class=LoggedRoute,
20+
responses=shared_responses,
21+
)
22+
```
23+
24+
## Authentication
25+
26+
Three tiers of auth dependency injection:
27+
28+
| Dependency | Returns | Use When |
29+
|-----------|---------|----------|
30+
| `get_current_user` | `Optional[UserData]` | Public endpoints that behave differently for authenticated users |
31+
| `require_current_user` | `UserData` | Endpoints requiring login |
32+
| `require_current_user_with_email` | `UserData` | Endpoints requiring verified email (write operations) |
33+
34+
Auth supports two mechanisms:
35+
- **ORCID JWT tokens** — primary auth for web users
36+
- **API keys** — for programmatic access
37+
38+
```python
39+
@router.get("/{urn}")
40+
def get_score_set(
41+
urn: str,
42+
db: Session = Depends(get_db),
43+
user: Optional[UserData] = Depends(get_current_user),
44+
):
45+
...
46+
```
47+
48+
## Authorization
49+
50+
Permission checks use the `assert_permission()` helper with an `Action` enum:
51+
52+
```python
53+
from mavedb.lib.authorization import assert_permission, Action
54+
55+
assert_permission(user, item, Action.READ) # View
56+
assert_permission(user, item, Action.UPDATE) # Modify
57+
assert_permission(user, item, Action.DELETE) # Delete
58+
assert_permission(user, item, Action.ADD_ROLE) # Manage contributors
59+
```
60+
61+
Key authorization behaviors:
62+
- **Private resources return 404** (not 403) to prevent information leakage about existence
63+
- Permission logic dispatches by resource type (ExperimentSet, Experiment, ScoreSet, etc.)
64+
- Admins bypass most permission checks
65+
66+
## Endpoint Patterns
67+
68+
### Standard CRUD
69+
```python
70+
@router.get("/", response_model=list[ScoreSetShortModel])
71+
def list_score_sets(db: Session = Depends(get_db)): ...
72+
73+
@router.get("/{urn}", response_model=ScoreSetFullModel)
74+
def get_score_set(urn: str, db: Session = Depends(get_db)): ...
75+
76+
@router.post("/", response_model=ScoreSetSavedModel, status_code=201)
77+
def create_score_set(body: ScoreSetCreateModel, db: Session = Depends(get_db)): ...
78+
79+
@router.put("/{urn}", response_model=ScoreSetSavedModel)
80+
def update_score_set(urn: str, body: ScoreSetUpdateModel, db: Session = Depends(get_db)): ...
81+
82+
@router.delete("/{urn}", status_code=204)
83+
def delete_score_set(urn: str, db: Session = Depends(get_db)): ...
84+
```
85+
86+
### Background Job Enqueueing
87+
For operations that trigger async processing:
88+
```python
89+
@router.post("/{urn}:publish")
90+
async def publish_score_set(
91+
urn: str,
92+
db: Session = Depends(get_db),
93+
user: UserData = Depends(require_current_user_with_email),
94+
worker: ArqRedis = Depends(get_worker),
95+
):
96+
# ... validation and DB updates ...
97+
await worker.enqueue_job(
98+
"create_variants_for_score_set",
99+
score_set.id,
100+
correlation_id,
101+
)
102+
```
103+
104+
### Error Responses
105+
Shared error response definitions are used across routers:
106+
```python
107+
responses=shared_responses # Defines 4xx/5xx response schemas
108+
```
109+
110+
## Worker Integration
111+
112+
### Job Pipeline
113+
Many operations chain through multiple worker jobs:
114+
1. `create_variants_for_score_set` — Parse uploaded CSV, create variant records
115+
2. `map_variants_for_score_set` — Map variants via DCD Mapping / VRS
116+
3. `submit_score_set_mappings_to_*` — Submit to ClinGen services
117+
118+
### Job Patterns
119+
```python
120+
async def create_variants_for_score_set(ctx: dict, score_set_id: int, correlation_id: str):
121+
logging_context = setup_job_state(ctx, correlation_id)
122+
db = ctx["db"]
123+
124+
try:
125+
# ... processing ...
126+
pass
127+
except Exception as e:
128+
send_slack_error(e, logging_context)
129+
raise
130+
```
131+
132+
### Backoff and Retry
133+
Use `enqueue_job_with_backoff()` for jobs that may need retries (e.g., external service calls).
134+
135+
## Correlation IDs
136+
Every request gets a correlation ID via starlette-context middleware. Pass it to worker jobs for end-to-end request tracing:
137+
```python
138+
from mavedb.lib.logging.context import save_to_logging_context
139+
correlation_id = save_to_logging_context({"score_set_urn": urn})
140+
```

0 commit comments

Comments
 (0)