Make portfolio invitation specific to portfolio

- add a base domain class
- extract shared model code to mixin
- rename invitation classes
- invitation model relationship to portfolio_role name is now more
  generic "role"
This commit is contained in:
dandds
2019-04-22 14:54:37 -04:00
parent ac36e34c13
commit c4ad7b4378
20 changed files with 228 additions and 203 deletions

View File

@@ -5,14 +5,16 @@ Base = declarative_base()
from .permissions import Permissions
from .permission_set import PermissionSet
from .user import User
from .portfolio_role import PortfolioRole
from .application_role import ApplicationRole
from .portfolio_role import PortfolioRole, Status as PortfolioRoleStatus
from .application_role import ApplicationRole, Status as ApplicationRoleStatus
from .environment_role import EnvironmentRole
from .portfolio import Portfolio
from .application import Application
from .environment import Environment
from .attachment import Attachment
from .audit_event import AuditEvent
from .invitation import Invitation
from .portfolio_invitation import PortfolioInvitation
from .task_order import TaskOrder
from .dd_254 import DD254
from .mixins.invites import Status as InvitationStatus

View File

@@ -2,3 +2,4 @@ from .timestamps import TimestampsMixin
from .auditable import AuditableMixin
from .permissions import PermissionsMixin
from .deletable import DeletableMixin
from .invites import InvitesMixin

View File

@@ -3,12 +3,11 @@ from enum import Enum
import secrets
from sqlalchemy import Column, ForeignKey, Enum as SQLAEnum, TIMESTAMP, String
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship, backref
from sqlalchemy.orm import relationship
from atst.models import Base, types
from atst.models.mixins.timestamps import TimestampsMixin
from atst.models.mixins.auditable import AuditableMixin
from atst.models import types
class Status(Enum):
@@ -19,24 +18,24 @@ class Status(Enum):
REJECTED_EXPIRED = "rejected_expired"
class Invitation(Base, TimestampsMixin, AuditableMixin):
__tablename__ = "invitations"
class InvitesMixin(object):
id = types.Id()
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
user = relationship("User", backref="invitations", foreign_keys=[user_id])
@declared_attr
def user_id(cls):
return Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
portfolio_role_id = Column(
UUID(as_uuid=True), ForeignKey("portfolio_roles.id"), index=True
)
portfolio_role = relationship(
"PortfolioRole",
backref=backref("invitations", order_by="Invitation.time_created"),
)
@declared_attr
def user(cls):
return relationship("User", foreign_keys=[cls.user_id])
inviter_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
inviter = relationship("User", backref="sent_invites", foreign_keys=[inviter_id])
@declared_attr
def inviter_id(cls):
return Column(UUID(as_uuid=True), ForeignKey("users.id"), index=True)
@declared_attr
def inviter(cls):
return relationship("User", foreign_keys=[cls.inviter_id])
status = Column(SQLAEnum(Status, native_enum=False, default=Status.PENDING))
@@ -47,8 +46,9 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
email = Column(String, nullable=False)
def __repr__(self):
return "<Invitation(user='{}', portfolio_role='{}', id='{}', email='{}')>".format(
self.user_id, self.portfolio_role_id, self.id, self.email
role_id = self.role.id if self.role else None
return "<{}(user='{}', role='{}', id='{}', email='{}')>".format(
self.__class__.__name__, self.user_id, role_id, self.id, self.email
)
@property
@@ -90,14 +90,9 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
Status.REVOKED,
]
@property
def portfolio(self):
if self.portfolio_role: # pragma: no branch
return self.portfolio_role.portfolio
@property
def user_name(self):
return self.portfolio_role.user.full_name
return self.role.user.full_name
@property
def is_revokable(self):
@@ -110,21 +105,3 @@ class Invitation(Base, TimestampsMixin, AuditableMixin):
@property
def user_dod_id(self):
return self.user.dod_id if self.user is not None else None
@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
@property
def portfolio_id(self):
return self.portfolio_role.portfolio_id

View File

@@ -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 PortfolioInvitation(Base, TimestampsMixin, AuditableMixin, InvitesMixin):
__tablename__ = "invitations"
portfolio_role_id = Column(
UUID(as_uuid=True), ForeignKey("portfolio_roles.id"), index=True
)
role = relationship(
"PortfolioRole",
backref=backref("invitations", order_by="PortfolioInvitation.time_created"),
)
@property
def portfolio(self):
if self.role: # pragma: no branch
return self.role.portfolio
@property
def portfolio_id(self):
return self.role.portfolio_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

View File

@@ -4,6 +4,7 @@ from sqlalchemy.dialects.postgresql import UUID
from atst.models import Base, types, mixins
from atst.models.permissions import Permissions
from atst.models.portfolio_invitation import PortfolioInvitation
users_permission_sets = Table(
@@ -31,6 +32,13 @@ class User(
primaryjoin="and_(ApplicationRole.user_id==User.id, ApplicationRole.deleted==False)",
)
portfolio_invitations = relationship(
"PortfolioInvitation", foreign_keys=PortfolioInvitation.user_id
)
sent_portfolio_invitations = relationship(
"PortfolioInvitation", foreign_keys=PortfolioInvitation.inviter_id
)
email = Column(String)
dod_id = Column(String, unique=True, nullable=False)
first_name = Column(String)