Add project field to list_notes API response #118

Merged
forgejo_admin merged 1 commit from 117-add-project-field-to-list-notes-api-resp into main 2026-03-08 16:20:17 +00:00

Summary

Adds a lightweight project field (slug + name) to the NoteSummary schema returned by GET /notes, so operators can audit project distribution across all notes without calling get_note 262 times individually.

Changes

  • src/pal_e_docs/schemas.py -- Added ProjectSummary schema (slug, name only) and added project: ProjectSummary | None to NoteSummary
  • src/pal_e_docs/routes/notes.py -- Added joinedload(Note.project) to the list_notes query to eagerly load project data
  • tests/test_list_notes_project.py -- 6 new unit tests covering assigned notes, unassigned notes, project filter, lightweight schema shape, mixed results, and existing filter compatibility

Test Plan

  • 6 new tests pass (test_list_notes_project.py)
  • Full suite passes (540 tests, 0 failures)
  • ruff lint + format clean
  • Verify on deployed instance: GET /notes returns project: {slug, name} for assigned notes and project: null for unassigned

Review Checklist

  • Code changes match issue requirements
  • No unrelated changes included
  • Tests cover happy path and edge cases
  • Linting passes (ruff check + format)
  • No breaking changes to existing API consumers (get_note unchanged, new field is additive)
  • Plan: plan-2026-02-26-tf-modularize-postgres (traceability)
  • Forgejo issue: #117
## Summary Adds a lightweight `project` field (slug + name) to the `NoteSummary` schema returned by `GET /notes`, so operators can audit project distribution across all notes without calling `get_note` 262 times individually. ## Changes - **`src/pal_e_docs/schemas.py`** -- Added `ProjectSummary` schema (slug, name only) and added `project: ProjectSummary | None` to `NoteSummary` - **`src/pal_e_docs/routes/notes.py`** -- Added `joinedload(Note.project)` to the `list_notes` query to eagerly load project data - **`tests/test_list_notes_project.py`** -- 6 new unit tests covering assigned notes, unassigned notes, project filter, lightweight schema shape, mixed results, and existing filter compatibility ## Test Plan - [x] 6 new tests pass (`test_list_notes_project.py`) - [x] Full suite passes (540 tests, 0 failures) - [x] ruff lint + format clean - Verify on deployed instance: `GET /notes` returns `project: {slug, name}` for assigned notes and `project: null` for unassigned ## Review Checklist - [x] Code changes match issue requirements - [x] No unrelated changes included - [x] Tests cover happy path and edge cases - [x] Linting passes (ruff check + format) - [x] No breaking changes to existing API consumers (`get_note` unchanged, new field is additive) ## Related - Plan: `plan-2026-02-26-tf-modularize-postgres` (traceability) - Forgejo issue: #117
Add project field to list_notes API response
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
3c7492ea65
Include lightweight project info (slug, name) in NoteSummary schema so
operators can audit project assignments without fetching each note
individually. Adds ProjectSummary schema, eager-loads the project
relationship in the list_notes query, and includes 6 unit tests.

Closes #117

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

PR #118 Review

BLOCKERS

None.

NITS

  1. ProjectSummary placement -- The new schema class is inserted between NoteOut and NoteSummary. This is a reasonable location, but consider grouping it near the other Project* schemas (ProjectCreate, ProjectUpdate, ProjectOut) at the top of the file for discoverability. Non-blocking since the current placement keeps it adjacent to its only consumer (NoteSummary).

  2. joinedload + explicit join interaction -- When the ?project=alpha query param is used, the query does both joinedload(Note.project) (for eager-loading the relationship) and .join(Project, Note.project_id == Project.id) (for filtering). SQLAlchemy handles this correctly via aliased joins for joinedload, so there is no correctness issue. Just noting it for future readers -- this is the expected SQLAlchemy pattern.

SOP COMPLIANCE

  • Branch named after issue (117-add-project-field-to-list-notes-api-resp references issue #117)
  • PR body has: Summary, Changes, Test Plan, Related
  • Related section references the plan slug (plan-2026-02-26-tf-modularize-postgres)
  • Related section references Forgejo issue #117
  • Tests exist (6 new tests in tests/test_list_notes_project.py)
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes -- exactly 3 files changed, all directly related to the feature
  • Changes are additive and backward-compatible (project field defaults to None)

CODE REVIEW

Schema (schemas.py): ProjectSummary is a clean, minimal schema with only slug and name. It uses from_attributes = True which correctly allows Pydantic to read from the SQLAlchemy Project model. The project: ProjectSummary | None = None default on NoteSummary ensures backward compatibility.

Query (routes/notes.py): Adding joinedload(Note.project) to the existing query options is the right approach. It prevents N+1 queries when serializing the project field across all notes. The Note.project relationship exists in models.py (line 108) and maps to project_id FK.

Tests (test_list_notes_project.py): All 6 tests cover meaningful scenarios:

  • Assigned note returns project data
  • Unassigned note returns null
  • Project filter still works with the new field
  • Lightweight schema shape check (only slug + name keys)
  • Mixed assigned/unassigned results
  • Compatibility with existing tag and note_type filters

PR body reports 540 tests passing with 0 failures.

VERDICT: APPROVED

## PR #118 Review ### BLOCKERS None. ### NITS 1. **`ProjectSummary` placement** -- The new schema class is inserted between `NoteOut` and `NoteSummary`. This is a reasonable location, but consider grouping it near the other `Project*` schemas (`ProjectCreate`, `ProjectUpdate`, `ProjectOut`) at the top of the file for discoverability. Non-blocking since the current placement keeps it adjacent to its only consumer (`NoteSummary`). 2. **`joinedload` + explicit `join` interaction** -- When the `?project=alpha` query param is used, the query does both `joinedload(Note.project)` (for eager-loading the relationship) and `.join(Project, Note.project_id == Project.id)` (for filtering). SQLAlchemy handles this correctly via aliased joins for `joinedload`, so there is no correctness issue. Just noting it for future readers -- this is the expected SQLAlchemy pattern. ### SOP COMPLIANCE - [x] Branch named after issue (`117-add-project-field-to-list-notes-api-resp` references issue #117) - [x] PR body has: Summary, Changes, Test Plan, Related - [x] Related section references the plan slug (`plan-2026-02-26-tf-modularize-postgres`) - [x] Related section references Forgejo issue #117 - [x] Tests exist (6 new tests in `tests/test_list_notes_project.py`) - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes -- exactly 3 files changed, all directly related to the feature - [x] Changes are additive and backward-compatible (`project` field defaults to `None`) ### CODE REVIEW **Schema** (`schemas.py`): `ProjectSummary` is a clean, minimal schema with only `slug` and `name`. It uses `from_attributes = True` which correctly allows Pydantic to read from the SQLAlchemy `Project` model. The `project: ProjectSummary | None = None` default on `NoteSummary` ensures backward compatibility. **Query** (`routes/notes.py`): Adding `joinedload(Note.project)` to the existing query options is the right approach. It prevents N+1 queries when serializing the project field across all notes. The `Note.project` relationship exists in `models.py` (line 108) and maps to `project_id` FK. **Tests** (`test_list_notes_project.py`): All 6 tests cover meaningful scenarios: - Assigned note returns project data - Unassigned note returns `null` - Project filter still works with the new field - Lightweight schema shape check (only `slug` + `name` keys) - Mixed assigned/unassigned results - Compatibility with existing tag and note_type filters PR body reports 540 tests passing with 0 failures. ### VERDICT: APPROVED
forgejo_admin deleted branch 117-add-project-field-to-list-notes-api-resp 2026-03-08 16:20:17 +00:00
Sign in to join this conversation.
No description provided.