Merge pull request #450 from dod-ccpo/send-invite-to-email-provided-##161961059
Send invite to email provided ##161961059
This commit is contained in:
commit
8c9e74112c
50
alembic/versions/02d11579a581_add_email_to_invite.py
Normal file
50
alembic/versions/02d11579a581_add_email_to_invite.py
Normal file
@ -0,0 +1,50 @@
|
||||
"""Add email to invite
|
||||
|
||||
Revision ID: 02d11579a581
|
||||
Revises: 4f46aecb337f
|
||||
Create Date: 2018-11-19 14:51:33.178358
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.sql import text
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '02d11579a581'
|
||||
down_revision = '4f46aecb337f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('invitations', sa.Column('email', sa.String()))
|
||||
conn = op.get_bind()
|
||||
# Add non-null value to email column
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
insert into invitations (email)
|
||||
select u.email
|
||||
from invitations i
|
||||
inner join workspace_roles wr on i.workspace_role_id = wr.id
|
||||
inner join users u on wr.user_id = u.id
|
||||
where i.email is null;
|
||||
"""
|
||||
)
|
||||
)
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
update invitations
|
||||
set email = 'example@example.com'
|
||||
where email is null;
|
||||
"""
|
||||
)
|
||||
)
|
||||
op.alter_column('invitations', 'email', nullable=False)
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('invitations', 'email')
|
||||
# ### end Alembic commands ###
|
@ -54,13 +54,14 @@ class Invitations(object):
|
||||
return invite
|
||||
|
||||
@classmethod
|
||||
def create(cls, inviter, workspace_role):
|
||||
def create(cls, inviter, workspace_role, email):
|
||||
invite = Invitation(
|
||||
workspace_role=workspace_role,
|
||||
inviter=inviter,
|
||||
user=workspace_role.user,
|
||||
status=InvitationStatus.PENDING,
|
||||
expiration_time=Invitations.current_expiration_time(),
|
||||
email=email,
|
||||
)
|
||||
db.session.add(invite)
|
||||
db.session.commit()
|
||||
@ -120,4 +121,6 @@ class Invitations(object):
|
||||
previous_invitation = Invitations._get(token)
|
||||
Invitations._update_status(previous_invitation, InvitationStatus.REVOKED)
|
||||
|
||||
return Invitations.create(user, previous_invitation.workspace_role)
|
||||
return Invitations.create(
|
||||
user, previous_invitation.workspace_role, previous_invitation.email
|
||||
)
|
||||
|
@ -42,11 +42,13 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
|
||||
|
||||
expiration_time = Column(TIMESTAMP(timezone=True))
|
||||
|
||||
token = Column(String(), index=True, default=lambda: secrets.token_urlsafe())
|
||||
token = Column(String, index=True, default=lambda: secrets.token_urlsafe())
|
||||
|
||||
email = Column(String, nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Invitation(user='{}', workspace_role='{}', id='{}')>".format(
|
||||
self.user_id, self.workspace_role_id, self.id
|
||||
return "<Invitation(user='{}', workspace_role='{}', id='{}', email='{}')>".format(
|
||||
self.user_id, self.workspace_role_id, self.id, self.email
|
||||
)
|
||||
|
||||
@property
|
||||
@ -82,10 +84,6 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
|
||||
if self.workspace_role:
|
||||
return self.workspace_role.workspace
|
||||
|
||||
@property
|
||||
def user_email(self):
|
||||
return self.workspace_role.user.email
|
||||
|
||||
@property
|
||||
def user_name(self):
|
||||
return self.workspace_role.user.full_name
|
||||
|
@ -262,10 +262,8 @@ def create_member(workspace_id):
|
||||
if form.validate():
|
||||
try:
|
||||
new_member = Workspaces.create_member(user, workspace, form.data)
|
||||
invite = Invitations.create(user, new_member)
|
||||
send_invite_email(
|
||||
g.current_user.full_name, invite.token, new_member.user.email
|
||||
)
|
||||
invite = Invitations.create(user, new_member, form.data["email"])
|
||||
send_invite_email(g.current_user.full_name, invite.token, invite.email)
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
@ -381,7 +379,7 @@ def revoke_invitation(workspace_id, token):
|
||||
@bp.route("/workspaces/<workspace_id>/invitations/<token>/resend", methods=["POST"])
|
||||
def resend_invitation(workspace_id, token):
|
||||
invite = Invitations.resend(g.current_user, workspace_id, token)
|
||||
send_invite_email(g.current_user.full_name, invite.token, invite.user_email)
|
||||
send_invite_email(g.current_user.full_name, invite.token, invite.email)
|
||||
return redirect(
|
||||
url_for(
|
||||
"workspaces.workspace_members",
|
||||
|
@ -22,7 +22,7 @@ def test_create_invitation():
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
ws_role = WorkspaceRoleFactory.create(user=user, workspace=workspace)
|
||||
invite = Invitations.create(workspace.owner, ws_role)
|
||||
invite = Invitations.create(workspace.owner, ws_role, user.email)
|
||||
assert invite.user == user
|
||||
assert invite.workspace_role == ws_role
|
||||
assert invite.inviter == workspace.owner
|
||||
@ -34,7 +34,7 @@ def test_accept_invitation():
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
ws_role = WorkspaceRoleFactory.create(user=user, workspace=workspace)
|
||||
invite = Invitations.create(workspace.owner, ws_role)
|
||||
invite = Invitations.create(workspace.owner, ws_role, user.email)
|
||||
assert invite.is_pending
|
||||
accepted_invite = Invitations.accept(user, invite.token)
|
||||
assert accepted_invite.is_accepted
|
||||
@ -89,7 +89,7 @@ def test_accept_invitation_twice():
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
ws_role = WorkspaceRoleFactory.create(user=user, workspace=workspace)
|
||||
invite = Invitations.create(workspace.owner, ws_role)
|
||||
invite = Invitations.create(workspace.owner, ws_role, user.email)
|
||||
Invitations.accept(user, invite.token)
|
||||
with pytest.raises(InvitationError):
|
||||
Invitations.accept(user, invite.token)
|
||||
@ -99,7 +99,7 @@ def test_revoke_invitation():
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
ws_role = WorkspaceRoleFactory.create(user=user, workspace=workspace)
|
||||
invite = Invitations.create(workspace.owner, ws_role)
|
||||
invite = Invitations.create(workspace.owner, ws_role, user.email)
|
||||
assert invite.is_pending
|
||||
Invitations.revoke(invite.token)
|
||||
assert invite.is_revoked
|
||||
@ -109,7 +109,7 @@ def test_resend_invitation():
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
ws_role = WorkspaceRoleFactory.create(user=user, workspace=workspace)
|
||||
invite = Invitations.create(workspace.owner, ws_role)
|
||||
invite = Invitations.create(workspace.owner, ws_role, user.email)
|
||||
Invitations.resend(workspace.owner, workspace.id, invite.token)
|
||||
assert ws_role.invitations[0].is_revoked
|
||||
assert ws_role.invitations[1].is_pending
|
||||
|
@ -342,5 +342,6 @@ class InvitationFactory(Base):
|
||||
class Meta:
|
||||
model = Invitation
|
||||
|
||||
email = factory.Faker("email")
|
||||
status = InvitationStatus.PENDING
|
||||
expiration_time = Invitations.current_expiration_time()
|
||||
|
@ -1,5 +1,6 @@
|
||||
import datetime
|
||||
from flask import url_for
|
||||
import pytest
|
||||
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
@ -339,6 +340,29 @@ def test_existing_member_accepts_valid_invite(client, user_session):
|
||||
assert len(Workspaces.for_user(user)) == 1
|
||||
|
||||
|
||||
def test_existing_member_invite_sent_to_email_submitted_in_form(
|
||||
client, user_session, queue
|
||||
):
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
member_form_data = {
|
||||
"dod_id": user.dod_id,
|
||||
"first_name": user.first_name,
|
||||
"last_name": user.last_name,
|
||||
"workspace_role": "developer",
|
||||
"email": "example@example.com",
|
||||
}
|
||||
user_session(workspace.owner)
|
||||
client.post(
|
||||
url_for("workspaces.create_member", workspace_id=workspace.id),
|
||||
data={**member_form_data},
|
||||
)
|
||||
|
||||
assert user.email != "example@example.com"
|
||||
assert len(queue.get_queue().jobs[0].args[0]) == 1
|
||||
assert queue.get_queue().jobs[0].args[0][0] == "example@example.com"
|
||||
|
||||
|
||||
def test_new_member_accepts_valid_invite(monkeypatch, client, user_session):
|
||||
workspace = WorkspaceFactory.create()
|
||||
user_info = UserFactory.dictionary()
|
||||
@ -478,3 +502,32 @@ def test_resend_invitation_sends_email(client, user_session, queue):
|
||||
)
|
||||
|
||||
assert len(queue.get_queue()) == 1
|
||||
|
||||
|
||||
def test_existing_member_invite_resent_to_email_submitted_in_form(
|
||||
client, user_session, queue
|
||||
):
|
||||
workspace = WorkspaceFactory.create()
|
||||
user = UserFactory.create()
|
||||
ws_role = WorkspaceRoleFactory.create(
|
||||
user=user, workspace=workspace, status=WorkspaceRoleStatus.PENDING
|
||||
)
|
||||
invite = InvitationFactory.create(
|
||||
user_id=user.id,
|
||||
workspace_role_id=ws_role.id,
|
||||
status=InvitationStatus.PENDING,
|
||||
email="example@example.com",
|
||||
)
|
||||
user_session(workspace.owner)
|
||||
client.post(
|
||||
url_for(
|
||||
"workspaces.resend_invitation",
|
||||
workspace_id=workspace.id,
|
||||
token=invite.token,
|
||||
)
|
||||
)
|
||||
|
||||
send_mail_job = queue.get_queue().jobs[0]
|
||||
assert user.email != "example@example.com"
|
||||
assert send_mail_job.func.__func__.__name__ == "_send_mail"
|
||||
assert send_mail_job.args[0] == ["example@example.com"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user