DM-54400: Project, Build, and Edition models#138
Merged
jonathansick merged 20 commits intomainfrom Mar 18, 2026
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OrgRoleDependencyandAuthorizationService, with org membership resolution across users and groups./orgs/{org_slug}/for projects, builds, editions, and members with HATEOAS response links.ObjectStoreandUserInfoStorebackend protocols for S3-compatible storage and group membership resolution.Test plan
uv run --only-group=nox nox -s test— storage, service, and handler testsuv run --only-group=nox nox -s client_test— client model testsuv run --only-group=nox nox -s typing— mypy type checkinguv run --only-group=lint pre-commit run --all-files— linting