record expiration time on the invitation

This commit is contained in:
dandds 2018-10-26 13:43:40 -04:00
parent d5998ed370
commit 5c5f9c6c9c
5 changed files with 44 additions and 8 deletions

View File

@ -0,0 +1,28 @@
"""add expiration_time to invitation
Revision ID: e62bcc460c26
Revises: 03654d08f5ff
Create Date: 2018-10-26 13:37:39.015003
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e62bcc460c26'
down_revision = '03654d08f5ff'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('invitations', sa.Column('expiration_time', sa.TIMESTAMP(timezone=True), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('invitations', 'expiration_time')
# ### end Alembic commands ###

View File

@ -36,6 +36,7 @@ class Invitations(object):
inviter=inviter, inviter=inviter,
user=user, user=user,
status=InvitationStatus.PENDING, status=InvitationStatus.PENDING,
expiration_time=Invitations.current_expiration_time(),
) )
db.session.add(invite) db.session.add(invite)
db.session.commit() db.session.commit()
@ -46,7 +47,7 @@ class Invitations(object):
def accept(cls, invite_id): def accept(cls, invite_id):
invite = Invitations._get(invite_id) invite = Invitations._get(invite_id)
if Invitations._is_expired(invite): if invite.is_expired:
invite.status = InvitationStatus.REJECTED invite.status = InvitationStatus.REJECTED
elif invite.is_pending: elif invite.is_pending:
invite.status = InvitationStatus.ACCEPTED invite.status = InvitationStatus.ACCEPTED
@ -60,9 +61,7 @@ class Invitations(object):
return invite return invite
@classmethod @classmethod
def _is_expired(cls, invite): def current_expiration_time(cls):
time_created = invite.time_created return datetime.datetime.now() + datetime.timedelta(
expiration = datetime.datetime.now(time_created.tzinfo) - datetime.timedelta(
minutes=Invitations.EXPIRATION_LIMIT_MINUTES minutes=Invitations.EXPIRATION_LIMIT_MINUTES
) )
return invite.time_created < expiration

View File

@ -1,6 +1,7 @@
import datetime
from enum import Enum from enum import Enum
from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum, TIMESTAMP
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@ -31,6 +32,8 @@ class Invitation(Base, TimestampsMixin):
status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING)) status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING))
expiration_time = Column(TIMESTAMP(timezone=True))
def __repr__(self): def __repr__(self):
return "<Invitation(user='{}', workspace='{}', id='{}')>".format( return "<Invitation(user='{}', workspace='{}', id='{}')>".format(
self.user.id, self.workspace.id, self.id self.user.id, self.workspace.id, self.id
@ -51,3 +54,7 @@ class Invitation(Base, TimestampsMixin):
@property @property
def is_rejected(self): def is_rejected(self):
return self.status == Status.REJECTED return self.status == Status.REJECTED
@property
def is_expired(self):
return datetime.datetime.now(self.expiration_time.tzinfo) > self.expiration_time

View File

@ -30,11 +30,11 @@ def test_accept_expired_invitation():
workspace = WorkspaceFactory.create() workspace = WorkspaceFactory.create()
user = UserFactory.create() user = UserFactory.create()
increment = Invitations.EXPIRATION_LIMIT_MINUTES + 1 increment = Invitations.EXPIRATION_LIMIT_MINUTES + 1
created_at = datetime.datetime.now() - datetime.timedelta(minutes=increment) expiration_time = datetime.datetime.now() - datetime.timedelta(minutes=increment)
invite = InvitationFactory.create( invite = InvitationFactory.create(
workspace_id=workspace.id, workspace_id=workspace.id,
user_id=user.id, user_id=user.id,
time_created=created_at, expiration_time=expiration_time,
status=Status.PENDING, status=Status.PENDING,
) )
with pytest.raises(InvitationError): with pytest.raises(InvitationError):

View File

@ -22,6 +22,7 @@ from atst.models.workspace_role import WorkspaceRole
from atst.models.environment_role import EnvironmentRole from atst.models.environment_role import EnvironmentRole
from atst.models.invitation import Invitation, Status as InvitationStatus from atst.models.invitation import Invitation, Status as InvitationStatus
from atst.domain.workspaces import Workspaces from atst.domain.workspaces import Workspaces
from atst.domain.invitations import Invitations
class Base(factory.alchemy.SQLAlchemyModelFactory): class Base(factory.alchemy.SQLAlchemyModelFactory):
@ -341,3 +342,4 @@ class InvitationFactory(Base):
model = Invitation model = Invitation
status = InvitationStatus.PENDING status = InvitationStatus.PENDING
expiration_time = Invitations.current_expiration_time()