Small tweaks for adding a new application member:

- raise specific invitation type if invite not found in invitation domain classes
- more terse assignments of defaults in invitation service, smh
- terser margin expression for inline input fields
- sass formatting
- use translation for cancel link
- oxford comma for app team management permission explanation
- do not format environment roles with hyphens for role selection
- generalize some additional methods in the invitation domain base class
- use plain atst.models import path
This commit is contained in:
dandds 2019-04-30 14:49:53 -04:00
parent 5db4d9bab3
commit 4f304d747e
10 changed files with 23 additions and 30 deletions

View File

@ -4,6 +4,7 @@ from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models import ApplicationInvitation, InvitationStatus, PortfolioInvitation
from atst.domain.portfolio_roles import PortfolioRoles
from atst.domain.application_roles import ApplicationRoles
from .exceptions import NotFoundError
@ -40,6 +41,7 @@ class InvitationError(Exception):
class BaseInvitations(object):
model = None
role_domain_class = None
# number of minutes a given invitation is considered valid
EXPIRATION_LIMIT_MINUTES = 360
@ -48,7 +50,7 @@ class BaseInvitations(object):
try:
invite = db.session.query(cls.model).filter_by(token=token).one()
except NoResultFound:
raise NotFoundError("invite")
raise NotFoundError(cls.model.__tablename__)
return invite
@ -86,7 +88,7 @@ class BaseInvitations(object):
elif invite.is_pending: # pragma: no branch
cls._update_status(invite, InvitationStatus.ACCEPTED)
PortfolioRoles.enable(invite.role)
cls.role_domain_class.enable(invite.role)
return invite
@classmethod
@ -109,11 +111,11 @@ class BaseInvitations(object):
return cls._update_status(invite, InvitationStatus.REVOKED)
@classmethod
def lookup_by_portfolio_and_user(cls, portfolio, user):
role = PortfolioRoles.get(portfolio.id, user.id)
def lookup_by_resource_and_user(cls, resource, user):
role = cls.role_domain_class.get(resource.id, user.id)
if role.latest_invitation is None:
raise NotFoundError("invitation")
raise NotFoundError(cls.model.__tablename__)
return role.latest_invitation
@ -127,7 +129,9 @@ class BaseInvitations(object):
class PortfolioInvitations(BaseInvitations):
model = PortfolioInvitation
role_domain_class = PortfolioRoles
class ApplicationInvitations(BaseInvitations):
model = ApplicationInvitation
role_domain_class = ApplicationRoles

View File

@ -3,7 +3,7 @@ from wtforms.fields import FormField, FieldList, HiddenField, BooleanField
from .forms import BaseForm
from .member import NewForm as BaseNewMemberForm
from .data import FORMATTED_ENV_ROLES as ENV_ROLES
from .data import ENV_ROLES
from atst.forms.fields import SelectField
from atst.domain.permission_sets import PermissionSets
from atst.utils.localization import translate

View File

@ -218,6 +218,3 @@ REQUIRED_DISTRIBUTIONS = [
]
ENV_ROLES = [(role.value, role.value) for role in CSPRole] + [(None, "No access")]
FORMATTED_ENV_ROLES = [
(role.value, "- {} -".format(role.value)) for role in CSPRole
] + [(None, "- No Access -")]

View File

@ -8,7 +8,7 @@ from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.domain.permission_sets import PermissionSets
from atst.domain.exceptions import AlreadyExistsError
from atst.forms.application_member import NewForm as NewMemberForm
from atst.models.permissions import Permissions
from atst.models import Permissions
from atst.services.invitation import Invitation as InvitationService
from atst.utils.flash import formatted_flash as flash
from atst.utils.localization import translate

View File

@ -57,7 +57,7 @@ def resend_invite(task_order_id):
if not officer:
raise NotFoundError("officer")
invitation = PortfolioInvitations.lookup_by_portfolio_and_user(portfolio, officer)
invitation = PortfolioInvitations.lookup_by_resource_and_user(portfolio, officer)
if not invitation:
raise NotFoundError("invitation")

View File

@ -57,26 +57,18 @@ class Invitation:
if isinstance(member, PortfolioRole):
self.email_template = (
"emails/portfolio/invitation.txt"
if self.email_template is None
else self.email_template
self.email_template or "emails/portfolio/invitation.txt"
)
self.subject = (
"{} has invited you to a JEDI cloud portfolio"
if self.subject is None
else self.subject
self.subject or "{} has invited you to a JEDI cloud portfolio"
)
self.domain_class = PortfolioInvitations
elif isinstance(member, ApplicationRole):
self.email_template = (
"emails/application/invitation.txt"
if self.email_template is None
else self.email_template
self.email_template or "emails/application/invitation.txt"
)
self.subject = (
"{} has invited you to a JEDI cloud application"
if self.subject is None
else self.subject
self.subject or "{} has invited you to a JEDI cloud application"
)
self.domain_class = ApplicationInvitations

View File

@ -180,10 +180,10 @@
}
.input__inline-fields {
margin: 1rem 0 1rem 0;
margin: 1rem 0;
&.input__inline-fields--indented {
margin-left: 4*$gap;
margin-left: $gap*4;
}
&> fieldset.usa-input__choices label {

View File

@ -37,7 +37,7 @@
v-bind:disabled="invalid"
class='action-group__action usa-button'
value='Next'>
<a class='action-group__action icon-link icon-link--default' v-on:click="closeModal('{{ new_port_mem }}')">Cancel</a>
<a class='action-group__action icon-link icon-link--default' v-on:click="closeModal('{{ new_port_mem }}')">{{ "common.cancel" | translate }}</a>
</div>
{% endset %}
{% set step_two %}
@ -117,7 +117,7 @@
class='action-group__action usa-button'
form="add-app-mem"
value='Invite member'>
<a class='action-group__action icon-link icon-link--default' v-on:click="closeModal('{{ new_port_mem }}')">Cancel</a>
<a class='action-group__action icon-link icon-link--default' v-on:click="closeModal('{{ new_port_mem }}')">{{ "common.cancel" | translate }}</a>
<input
type='button'
v-on:click="previous()"

View File

@ -152,9 +152,9 @@ def test_lookup_by_user_and_portfolio():
ws_role = PortfolioRoleFactory.create(user=user, portfolio=portfolio)
invite = PortfolioInvitations.create(portfolio.owner, ws_role, user.email)
assert PortfolioInvitations.lookup_by_portfolio_and_user(portfolio, user) == invite
assert PortfolioInvitations.lookup_by_resource_and_user(portfolio, user) == invite
with pytest.raises(NotFoundError):
PortfolioInvitations.lookup_by_portfolio_and_user(
PortfolioInvitations.lookup_by_resource_and_user(
portfolio, UserFactory.create()
)

View File

@ -444,7 +444,7 @@ portfolios:
manage_perms: 'Manage permissions for {application_name}'
manage_envs: 'Allow member to <strong>add</strong> and <strong>rename environments</strong> within the application.'
delete_envs: 'Allow member to <strong>delete environments</strong> within the application.'
manage_team: 'Allow member to <strong>add, update</strong> and <strong>remove members</strong> from the application team.'
manage_team: 'Allow member to <strong>add, update,</strong> and <strong>remove members</strong> from the application team.'
index:
empty:
start_button: Start a new JEDI portfolio