From 5c5f9c6c9ccd0e4372fb83fa83f97171ebba1cfd Mon Sep 17 00:00:00 2001 From: dandds Date: Fri, 26 Oct 2018 13:43:40 -0400 Subject: [PATCH] record expiration time on the invitation --- ...60c26_add_expiration_time_to_invitation.py | 28 +++++++++++++++++++ atst/domain/invitations.py | 9 +++--- atst/models/invitation.py | 9 +++++- tests/domain/test_invitations.py | 4 +-- tests/factories.py | 2 ++ 5 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 alembic/versions/e62bcc460c26_add_expiration_time_to_invitation.py diff --git a/alembic/versions/e62bcc460c26_add_expiration_time_to_invitation.py b/alembic/versions/e62bcc460c26_add_expiration_time_to_invitation.py new file mode 100644 index 00000000..9b6fc4d1 --- /dev/null +++ b/alembic/versions/e62bcc460c26_add_expiration_time_to_invitation.py @@ -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 ### diff --git a/atst/domain/invitations.py b/atst/domain/invitations.py index c2e2be2e..fc54c43c 100644 --- a/atst/domain/invitations.py +++ b/atst/domain/invitations.py @@ -36,6 +36,7 @@ class Invitations(object): inviter=inviter, user=user, status=InvitationStatus.PENDING, + expiration_time=Invitations.current_expiration_time(), ) db.session.add(invite) db.session.commit() @@ -46,7 +47,7 @@ class Invitations(object): def accept(cls, invite_id): invite = Invitations._get(invite_id) - if Invitations._is_expired(invite): + if invite.is_expired: invite.status = InvitationStatus.REJECTED elif invite.is_pending: invite.status = InvitationStatus.ACCEPTED @@ -60,9 +61,7 @@ class Invitations(object): return invite @classmethod - def _is_expired(cls, invite): - time_created = invite.time_created - expiration = datetime.datetime.now(time_created.tzinfo) - datetime.timedelta( + def current_expiration_time(cls): + return datetime.datetime.now() + datetime.timedelta( minutes=Invitations.EXPIRATION_LIMIT_MINUTES ) - return invite.time_created < expiration diff --git a/atst/models/invitation.py b/atst/models/invitation.py index 0d8bc8d8..4f68e332 100644 --- a/atst/models/invitation.py +++ b/atst/models/invitation.py @@ -1,6 +1,7 @@ +import datetime 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.orm import relationship @@ -31,6 +32,8 @@ class Invitation(Base, TimestampsMixin): status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING)) + expiration_time = Column(TIMESTAMP(timezone=True)) + def __repr__(self): return "".format( self.user.id, self.workspace.id, self.id @@ -51,3 +54,7 @@ class Invitation(Base, TimestampsMixin): @property def is_rejected(self): return self.status == Status.REJECTED + + @property + def is_expired(self): + return datetime.datetime.now(self.expiration_time.tzinfo) > self.expiration_time diff --git a/tests/domain/test_invitations.py b/tests/domain/test_invitations.py index 864fa79d..cbcb43d4 100644 --- a/tests/domain/test_invitations.py +++ b/tests/domain/test_invitations.py @@ -30,11 +30,11 @@ def test_accept_expired_invitation(): workspace = WorkspaceFactory.create() user = UserFactory.create() 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( workspace_id=workspace.id, user_id=user.id, - time_created=created_at, + expiration_time=expiration_time, status=Status.PENDING, ) with pytest.raises(InvitationError): diff --git a/tests/factories.py b/tests/factories.py index eac728a9..babe6595 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -22,6 +22,7 @@ from atst.models.workspace_role import WorkspaceRole from atst.models.environment_role import EnvironmentRole from atst.models.invitation import Invitation, Status as InvitationStatus from atst.domain.workspaces import Workspaces +from atst.domain.invitations import Invitations class Base(factory.alchemy.SQLAlchemyModelFactory): @@ -341,3 +342,4 @@ class InvitationFactory(Base): model = Invitation status = InvitationStatus.PENDING + expiration_time = Invitations.current_expiration_time()