Create an organization invite for an email address.
(
payload: OrganizationInviteCreate,
session: AsyncSession = SESSION_DEP,
ctx: OrganizationContext = ORG_ADMIN_DEP,
)
| 607 | |
| 608 | @router.post("/me/invites", response_model=OrganizationInviteRead) |
| 609 | async def create_org_invite( |
| 610 | payload: OrganizationInviteCreate, |
| 611 | session: AsyncSession = SESSION_DEP, |
| 612 | ctx: OrganizationContext = ORG_ADMIN_DEP, |
| 613 | ) -> OrganizationInviteRead: |
| 614 | """Create an organization invite for an email address.""" |
| 615 | email = normalize_invited_email(payload.invited_email) |
| 616 | if not email: |
| 617 | raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT) |
| 618 | |
| 619 | existing_user = ( |
| 620 | await session.exec(select(User).where(func.lower(col(User.email)) == email)) |
| 621 | ).first() |
| 622 | if existing_user is not None: |
| 623 | existing_member = await get_member( |
| 624 | session, |
| 625 | user_id=existing_user.id, |
| 626 | organization_id=ctx.organization.id, |
| 627 | ) |
| 628 | if existing_member is not None: |
| 629 | raise HTTPException(status_code=status.HTTP_409_CONFLICT) |
| 630 | |
| 631 | token = secrets.token_urlsafe(24) |
| 632 | invite = OrganizationInvite( |
| 633 | organization_id=ctx.organization.id, |
| 634 | invited_email=email, |
| 635 | token=token, |
| 636 | role=normalize_role(payload.role), |
| 637 | all_boards_read=payload.all_boards_read, |
| 638 | all_boards_write=payload.all_boards_write, |
| 639 | created_by_user_id=ctx.member.user_id, |
| 640 | created_at=utcnow(), |
| 641 | updated_at=utcnow(), |
| 642 | ) |
| 643 | session.add(invite) |
| 644 | await session.flush() |
| 645 | |
| 646 | board_ids = {entry.board_id for entry in payload.board_access} |
| 647 | if board_ids: |
| 648 | valid_board_ids = { |
| 649 | board.id |
| 650 | for board in await Board.objects.filter_by( |
| 651 | organization_id=ctx.organization.id, |
| 652 | ) |
| 653 | .filter(col(Board.id).in_(board_ids)) |
| 654 | .all(session) |
| 655 | } |
| 656 | if valid_board_ids != board_ids: |
| 657 | raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT) |
| 658 | await apply_invite_board_access( |
| 659 | session, |
| 660 | invite=invite, |
| 661 | entries=payload.board_access, |
| 662 | ) |
| 663 | await session.commit() |
| 664 | await session.refresh(invite) |
| 665 | return OrganizationInviteRead.model_validate(invite, from_attributes=True) |
| 666 |
nothing calls this directly
no test coverage detected