Accept an invite and create membership plus scoped board access rows.
(
session: AsyncSession,
invite: OrganizationInvite,
user: User,
)
| 185 | |
| 186 | |
| 187 | async def accept_invite( |
| 188 | session: AsyncSession, |
| 189 | invite: OrganizationInvite, |
| 190 | user: User, |
| 191 | ) -> OrganizationMember: |
| 192 | """Accept an invite and create membership plus scoped board access rows.""" |
| 193 | now = utcnow() |
| 194 | member = OrganizationMember( |
| 195 | organization_id=invite.organization_id, |
| 196 | user_id=user.id, |
| 197 | role=invite.role, |
| 198 | all_boards_read=invite.all_boards_read, |
| 199 | all_boards_write=invite.all_boards_write, |
| 200 | created_at=now, |
| 201 | updated_at=now, |
| 202 | ) |
| 203 | session.add(member) |
| 204 | await session.flush() |
| 205 | |
| 206 | # For scoped invites, copy invite board-access rows onto the member at accept |
| 207 | # time so effective permissions survive invite lifecycle cleanup. |
| 208 | if not (invite.all_boards_read or invite.all_boards_write): |
| 209 | access_rows = list( |
| 210 | await session.exec( |
| 211 | select(OrganizationInviteBoardAccess).where( |
| 212 | col(OrganizationInviteBoardAccess.organization_invite_id) == invite.id, |
| 213 | ), |
| 214 | ), |
| 215 | ) |
| 216 | for row in access_rows: |
| 217 | session.add( |
| 218 | OrganizationBoardAccess( |
| 219 | organization_member_id=member.id, |
| 220 | board_id=row.board_id, |
| 221 | can_read=row.can_read, |
| 222 | can_write=row.can_write, |
| 223 | created_at=now, |
| 224 | updated_at=now, |
| 225 | ), |
| 226 | ) |
| 227 | |
| 228 | invite.accepted_by_user_id = user.id |
| 229 | invite.accepted_at = now |
| 230 | invite.updated_at = now |
| 231 | session.add(invite) |
| 232 | if user.active_organization_id is None: |
| 233 | user.active_organization_id = invite.organization_id |
| 234 | session.add(user) |
| 235 | await session.commit() |
| 236 | await session.refresh(member) |
| 237 | return member |
| 238 | |
| 239 | |
| 240 | def _get_default_skill_pack_records(org_id: UUID, now: "datetime") -> list[SkillPack]: |
no test coverage detected