Skip to content

DM-54400: Project, Build, and Edition models#138

Merged
jonathansick merged 20 commits intomainfrom
tickets/DM-54400
Mar 18, 2026
Merged

DM-54400: Project, Build, and Edition models#138
jonathansick merged 20 commits intomainfrom
tickets/DM-54400

Conversation

@jonathansick
Copy link
Copy Markdown
Member

Summary

  • Add the four core domain entities — projects, builds, editions, and organization memberships — across the full stack: client models, domain models, DB schema (with Alembic migration), storage stores, services, and REST handlers.
  • Add role-based authorization (reader / uploader / admin) via OrgRoleDependency and AuthorizationService, with org membership resolution across users and groups.
  • Add org-scoped REST endpoints under /orgs/{org_slug}/ for projects, builds, editions, and members with HATEOAS response links.
  • Add ObjectStore and UserInfoStore backend protocols for S3-compatible storage and group membership resolution.

Test plan

  • uv run --only-group=nox nox -s test — storage, service, and handler tests
  • uv run --only-group=nox nox -s client_test — client model tests
  • uv run --only-group=nox nox -s typing — mypy type checking
  • uv run --only-group=lint pre-commit run --all-files — linting

Add pydantic request/response models and enums for the four core
domain entities: projects, builds, editions, and org memberships.
These client models define the API contract and are imported by
domain models, storage, services, and handlers.
Add pydantic domain models (Project, Build, Edition, OrgMembership),
SQLAlchemy ORM classes (SqlProject, SqlBuild, SqlEdition,
SqlOrgMembership), and the Alembic migration for the projects, builds,
editions, and org_memberships tables. Also add new exception types:
InvalidBuildStateError, PermissionDeniedError, and ConflictError.
Add data access stores (ProjectStore, BuildStore, EditionStore,
OrgMembershipStore) and backend protocols (ObjectStore, UserInfoStore)
for the storage layer. Includes comprehensive tests for all four
store implementations covering CRUD operations, soft delete, status
transitions, and role resolution.
Add business logic services for projects (ProjectService), builds
(BuildService), editions (EditionService), and authorization
(AuthorizationService). The authorization service enforces role-based
access control using OrgMembershipStore with role hierarchy
(reader < uploader < admin). Includes tests for role enforcement.
Add org-scoped REST endpoints for projects, builds, editions, and
members under /orgs/{org_slug}/. Add OrgRoleDependency for role-based
auth enforcement using X-Auth-Request-User/Token headers. Wire new
services and stores into Factory and RequestContext. Register the
orgs_router in the application. Includes handler tests for all four
resource types and updated test fixtures with seed_org_with_admin.
The initial build status is now `pending` (client is uploading) and
clients send `{"status": "uploaded"}` via PATCH to signal upload
completion, which triggers the transition to `processing`. This
aligns with the SQR-112 design and removes the confusing reuse of
`uploading` as both the initial state and the completion signal.
git_ref mode now excludes alternate-scoped builds, and
alternate_git_ref mode checks both git_ref and alternate_name.
Services now accept URL-level identifiers (org_slug, project_slug,
base32 build_id) and raise NotFoundError/ConflictError directly.
Handlers are thin wrappers that call a single service method and
return the response. BuildService.signal_upload_complete owns the
full queue integration flow.
Implement the organization detail endpoint per SQR-112 so that
org_url HATEOAS fields in Project and OrgMembership responses
point to a reader-accessible route instead of the admin endpoint.
Add cursor-based pagination to projects, editions, and builds list
endpoints using Safir's CountedPaginatedQueryRunner. Each endpoint
now returns Link (RFC 8288) and X-Total-Count response headers.

Projects and editions support configurable sort order (slug or
date_created; editions also date_updated). Editions can be filtered
by kind, builds by status. Pagination cursors, sort order enums, and
constants live in a new storage/pagination module that handlers
reference directly while stores receive cursor types as parameters.
jonathansick added a commit to lsst-sqre/phalanx that referenced this pull request Mar 17, 2026
Use FastAPI's Path(alias=...) to expose cleaner parameter names in the
OpenAPI spec (org, project, edition, build, member, job) while keeping
descriptive Python variable names internally. Shared Annotated type
aliases are defined in handlers/params.py for DRY reuse across handlers.
Move tag assignment from the parent APIRouter to individual
include_router calls so org/member endpoints are tagged "orgs"
and project/build/edition endpoints are tagged "projects".
Introduce a `q` query parameter that performs trigram similarity
search across project slug and title fields, ranked by relevance.
When `q` is provided, keyset pagination is disabled (422 if combined
with `cursor`). Results are capped at `limit` with X-Total-Count
reporting total matches.
Update the vague "Alternate identifier for the build." to match the
BuildCreate description: "Deployment variant scope for the build."
Add tests verifying that each endpoint enforces the correct minimum
role: reader for GET, uploader for build create/patch, admin for
project/edition/member mutations. Also adds a seed_member helper to
tests/conftest.py for creating users with arbitrary roles.
Verify that creating a project with a slug that already exists in the
same org returns 409 Conflict.
Search via the `q` parameter on GET /orgs/{org}/projects now supports
keyset cursor pagination using a compound (score, id) cursor, removing
the previous silent truncation at the limit cap.

The cursor score is cast to REAL (float4) in SQL comparisons to match
PostgreSQL's similarity() return type and avoid float8/float4 precision
mismatches during keyset filtering.
@jonathansick jonathansick merged commit 2225974 into main Mar 18, 2026
15 checks passed
@jonathansick jonathansick deleted the tickets/DM-54400 branch March 18, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant