feat(organizations): add organization_project_id FK to team_project_roles - #20540
Draft
tripleaceme wants to merge 1 commit into
Draft
tripleaceme wants to merge 1 commit into
tripleaceme wants to merge 1 commit into
Conversation
…oles Team project roles link a team to a project, but nothing tied them to the org-project association they depend on. Removing a project from an organization deletes neither a team nor a project, so the existing cascades never fired and the cleanup had to be done by hand in delete_organization_project. Add an organization_project_id FK to organization_projects with ON DELETE CASCADE so the database enforces the invariant, backfill it by joining teams.organization_id through organization_projects, and populate it for new roles. The column is nullable for now. Migrations run while the previous version of the code is still serving, and that version inserts these rows without the column, so NOT NULL would break inserts mid-deploy. A follow-up migration sets NOT NULL once every writer populates it and the remaining orphaned rows are resolved. Refs pypi#19748 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Refs #19748. This is phase 1 of 2.
What this does
Adds
team_project_roles.organization_project_id, a FK toorganization_projects.idwithON DELETE CASCADE, so the database can enforce the invariant that a team's project role cannot outlive the org-project association it depends on.TeamProjectRolegains the column, an index, and anorganization_projectrelationshipteams.organization_id→organization_projects, then creates the index and FKadd_team_project_rolepopulates the column for new rolesWhy the column is nullable
I originally planned
NOT NULL, but the migration header in this repo rules it out for a single PR:The previous version of the code inserts
TeamProjectRolerows without this column, so aNOT NULLcolumn with no default would break those inserts mid-deploy. Following the guidance to break backwards-incompatible changes across multiple PRs, this one only expands.What is deliberately left for phase 2
NOT NULL. Once every writer populates the column.(team.organization_id, project_id)has no matchingorganization_projectsrow are exactly the stale grants this issue describes. The backfill here leaves themNULLrather than deleting them, so this migration performs no destructive writes against production data. I'd rather surface the count and have you decide than delete rows unilaterally — happy to add a reporting step if useful.delete(TeamProjectRole)indelete_organization_projectstays, since it is still the only thing covering rows where the column isNULL. The comment there now says so.OIDCPublisherProjectAssociation(item 4 in the issue). Publishers aren't inherently org-scoped, and bundling it would make this much harder to review. Happy to do it separately.Testing
Three tests in
tests/unit/organizations/test_services.py:add_team_project_rolelinks the association when the project belongs to the team's organizationadd_team_project_roleleaves the columnNULLwhen there is no association (valid while nullable)OrganizationProjectdirectly — bypassing the service-layer cleanup — cascades the role away, which is the actual invariantI left
TeamProjectRoleFactoryalone on purpose. Having it auto-create anOrganizationProjectlooked tempting, but several existing tests build a team viaTeamFactory.create()with no organization, so the team invents its own org distinct from the one the project was added to. The factory would then give one project two rows inorganization_projects, andProject.organizationisuselist=Falseover that table. Tests that need the link now pass it explicitly.Notes
team_project_rolesis organization-only, so the backfillUPDATEshould be well inside the default 5s statement timeout, but flagging it in case the production row count says otherwise.Opening as a draft while I finish running the suite locally — I'll mark it ready for review once it's green. Feedback on the phasing and on the orphan question above is welcome in the meantime.
🤖 Generated with Claude Code