From 054f6b80b912b4be5784f3b8410cc2f1d8f03552 Mon Sep 17 00:00:00 2001 From: dandds Date: Tue, 23 Apr 2019 09:28:56 -0400 Subject: [PATCH] add application_invitation table --- ...432c5287256d_add_application_invitation.py | 69 +++++++++++++++++++ atst/models/__init__.py | 1 + atst/models/application_invitation.py | 41 +++++++++++ atst/models/portfolio_invitation.py | 2 +- tests/factories.py | 9 +++ 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 alembic/versions/432c5287256d_add_application_invitation.py create mode 100644 atst/models/application_invitation.py diff --git a/alembic/versions/432c5287256d_add_application_invitation.py b/alembic/versions/432c5287256d_add_application_invitation.py new file mode 100644 index 00000000..fac3b326 --- /dev/null +++ b/alembic/versions/432c5287256d_add_application_invitation.py @@ -0,0 +1,69 @@ +"""add application_invitation + +Revision ID: 432c5287256d +Revises: 1880551a32e4 +Create Date: 2019-04-23 09:23:05.738680 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '432c5287256d' +down_revision = '1880551a32e4' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.rename_table('invitations', 'portfolio_invitations') + op.create_table('application_invitations', + sa.Column('time_created', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), + sa.Column('time_updated', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), + sa.Column('id', postgresql.UUID(as_uuid=True), server_default=sa.text('uuid_generate_v4()'), nullable=False), + sa.Column('status', sa.Enum('ACCEPTED', 'REVOKED', 'PENDING', 'REJECTED_WRONG_USER', 'REJECTED_EXPIRED', name='status', native_enum=False), nullable=True), + sa.Column('expiration_time', sa.TIMESTAMP(timezone=True), nullable=True), + sa.Column('token', sa.String(), nullable=True), + sa.Column('email', sa.String(), nullable=False), + sa.Column('application_role_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('inviter_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.ForeignKeyConstraint(['application_role_id'], ['application_roles.id'], ), + sa.ForeignKeyConstraint(['inviter_id'], ['users.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_application_invitations_application_role_id'), 'application_invitations', ['application_role_id'], unique=False) + op.create_index(op.f('ix_application_invitations_inviter_id'), 'application_invitations', ['inviter_id'], unique=False) + op.create_index(op.f('ix_application_invitations_token'), 'application_invitations', ['token'], unique=False) + op.create_index(op.f('ix_application_invitations_user_id'), 'application_invitations', ['user_id'], unique=False) + op.drop_index('ix_invitations_inviter_id', table_name='invitations') + op.drop_index('ix_invitations_portfolio_role_id', table_name='invitations') + op.drop_index('ix_invitations_token', table_name='invitations') + op.drop_index('ix_invitations_user_id', table_name='invitations') + op.create_index(op.f('ix_portfolio_invitations_inviter_id'), 'portfolio_invitations', ['inviter_id'], unique=False) + op.create_index(op.f('ix_portfolio_invitations_portfolio_role_id'), 'portfolio_invitations', ['portfolio_role_id'], unique=False) + op.create_index(op.f('ix_portfolio_invitations_token'), 'portfolio_invitations', ['token'], unique=False) + op.create_index(op.f('ix_portfolio_invitations_user_id'), 'portfolio_invitations', ['user_id'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.rename_table('portfolio_invitations', 'invitations') + op.drop_index(op.f('ix_application_invitations_user_id'), table_name='application_invitations') + op.drop_index(op.f('ix_application_invitations_token'), table_name='application_invitations') + op.drop_index(op.f('ix_application_invitations_inviter_id'), table_name='application_invitations') + op.drop_index(op.f('ix_application_invitations_application_role_id'), table_name='application_invitations') + op.drop_table('application_invitations') + op.drop_index(op.f('ix_portfolio_invitations_user_id'), table_name='portfolio_invitations') + op.drop_index(op.f('ix_portfolio_invitations_token'), table_name='portfolio_invitations') + op.drop_index(op.f('ix_portfolio_invitations_portfolio_role_id'), table_name='portfolio_invitations') + op.drop_index(op.f('ix_portfolio_invitations_inviter_id'), table_name='portfolio_invitations') + op.create_index('ix_invitations_user_id', 'invitations', ['user_id'], unique=False) + op.create_index('ix_invitations_token', 'invitations', ['token'], unique=False) + op.create_index('ix_invitations_portfolio_role_id', 'invitations', ['portfolio_role_id'], unique=False) + op.create_index('ix_invitations_inviter_id', 'invitations', ['inviter_id'], unique=False) + # ### end Alembic commands ### diff --git a/atst/models/__init__.py b/atst/models/__init__.py index 0369bc07..7fe45e05 100644 --- a/atst/models/__init__.py +++ b/atst/models/__init__.py @@ -14,6 +14,7 @@ from .environment import Environment from .attachment import Attachment from .audit_event import AuditEvent from .portfolio_invitation import PortfolioInvitation +from .application_invitation import ApplicationInvitation from .task_order import TaskOrder from .dd_254 import DD254 diff --git a/atst/models/application_invitation.py b/atst/models/application_invitation.py new file mode 100644 index 00000000..9f1db550 --- /dev/null +++ b/atst/models/application_invitation.py @@ -0,0 +1,41 @@ +from sqlalchemy import Column, ForeignKey +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship, backref + +from atst.models import Base +from atst.models.mixins import TimestampsMixin, AuditableMixin, InvitesMixin + + +class ApplicationInvitation(Base, TimestampsMixin, AuditableMixin, InvitesMixin): + __tablename__ = "application_invitations" + + application_role_id = Column( + UUID(as_uuid=True), ForeignKey("application_roles.id"), index=True + ) + role = relationship( + "ApplicationRole", + backref=backref("invitations", order_by="ApplicationInvitation.time_created"), + ) + + @property + def application(self): + if self.role: # pragma: no branch + return self.role.application + + @property + def application_id(self): + return self.role.application_id + + @property + def event_details(self): + return {"email": self.email, "dod_id": self.user_dod_id} + + @property + def history(self): + changes = self.get_changes() + change_set = {} + + if "status" in changes: + change_set["status"] = [s.name for s in changes["status"]] + + return change_set diff --git a/atst/models/portfolio_invitation.py b/atst/models/portfolio_invitation.py index 916e63ae..1c1e43dd 100644 --- a/atst/models/portfolio_invitation.py +++ b/atst/models/portfolio_invitation.py @@ -7,7 +7,7 @@ from atst.models.mixins import TimestampsMixin, AuditableMixin, InvitesMixin class PortfolioInvitation(Base, TimestampsMixin, AuditableMixin, InvitesMixin): - __tablename__ = "invitations" + __tablename__ = "portfolio_invitations" portfolio_role_id = Column( UUID(as_uuid=True), ForeignKey("portfolio_roles.id"), index=True diff --git a/tests/factories.py b/tests/factories.py index 89999c63..8bdf9310 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -246,6 +246,15 @@ class PortfolioInvitationFactory(Base): expiration_time = PortfolioInvitations.current_expiration_time() +class ApplicationInvitationFactory(Base): + class Meta: + model = ApplicationInvitation + + email = factory.Faker("email") + status = InvitationStatus.PENDING + expiration_time = PortfolioInvitations.current_expiration_time() + + class AttachmentFactory(Base): class Meta: model = Attachment