Merge pull request #1192 from dod-ccpo/app-member-status-labels

Status labels on app members table
This commit is contained in:
leigh-mil 2019-11-22 08:23:26 -05:00 committed by GitHub
commit c2975fff69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 100 additions and 21 deletions

View File

@ -102,6 +102,37 @@ class ApplicationRole(
"portfolio": self.application.portfolio.name, "portfolio": self.application.portfolio.name,
} }
@property
def is_pending(self):
return self.status == Status.PENDING
@property
def is_active(self):
return self.status == Status.ACTIVE
@property
def display_status(self):
if (
self.is_pending
and self.latest_invitation
and self.latest_invitation.is_pending
):
return "invite_pending"
elif (
self.is_pending
and self.latest_invitation
and self.latest_invitation.is_expired
):
return "invite_expired"
elif self.is_active and any(
env_role.is_pending for env_role in self.environment_roles
):
return "changes_pending"
return None
Index( Index(
"application_role_user_application", "application_role_user_application",

View File

@ -70,6 +70,10 @@ class EnvironmentRole(
def disabled(self): def disabled(self):
return self.status == EnvironmentRole.Status.DISABLED return self.status == EnvironmentRole.Status.DISABLED
@property
def is_pending(self):
return self.status == EnvironmentRole.Status.PENDING
@property @property
def event_details(self): def event_details(self):
return { return {

View File

@ -122,7 +122,7 @@ def get_members_data(application):
"user_name": member.user_name, "user_name": member.user_name,
"permission_sets": permission_sets, "permission_sets": permission_sets,
"environment_roles": environment_roles, "environment_roles": environment_roles,
"role_status": member.status.value, "role_status": member.display_status,
"form": form, "form": form,
"update_invite_form": update_invite_form, "update_invite_form": update_invite_form,
} }

View File

@ -20,6 +20,10 @@
white-space: nowrap; white-space: nowrap;
text-transform: uppercase; text-transform: uppercase;
&--default {
background-color: $color-gray-dark;
}
&--info { &--info {
background-color: $color-primary; background-color: $color-primary;
} }

View File

@ -54,7 +54,7 @@
{%- endif %} {%- endif %}
<br> <br>
{% if env['pending'] -%} {% if env['pending'] -%}
{{ Label('exchange', 'Changes Pending', classes='label--below')}} {{ Label(type="changes_pending", classes='label--below')}}
{% else %} {% else %}
<a href='{{ url_for("applications.access_environment", environment_id=env.id)}}' target='_blank' rel='noopener noreferrer' class='application-list-item__environment__csp_link'> <a href='{{ url_for("applications.access_environment", environment_id=env.id)}}' target='_blank' rel='noopener noreferrer' class='application-list-item__environment__csp_link'>
<span>{{ "portfolios.applications.csp_link" | translate }} {{ Icon('link', classes="icon--tiny") }}</span> <span>{{ "portfolios.applications.csp_link" | translate }} {{ Icon('link', classes="icon--tiny") }}</span>

View File

@ -33,6 +33,8 @@
{% else %} {% else %}
{% for member in members %} {% for member in members %}
{% set invite_pending = member.role_status == 'invite_pending' %}
{% set invite_expired = member.role_status == 'invite_expired' %}
{%- if user_can(permissions.EDIT_APPLICATION_MEMBER) %} {%- if user_can(permissions.EDIT_APPLICATION_MEMBER) %}
{% set modal_name = "edit_member-{}".format(loop.index) %} {% set modal_name = "edit_member-{}".format(loop.index) %}
{% call Modal(modal_name, classes="form-content--app-mem") %} {% call Modal(modal_name, classes="form-content--app-mem") %}
@ -52,7 +54,7 @@
</base-form> </base-form>
{% endcall %} {% endcall %}
{%- if member.role_status == 'pending' %} {%- if invite_pending or invite_expired %}
{% set resend_invite_modal = "resend_invite-{}".format(member.role_id) %} {% set resend_invite_modal = "resend_invite-{}".format(member.role_id) %}
{% call Modal(resend_invite_modal, classes="form-content--app-mem") %} {% call Modal(resend_invite_modal, classes="form-content--app-mem") %}
<div class="modal__form--header"> <div class="modal__form--header">
@ -73,7 +75,7 @@
{% endif -%} {% endif -%}
{% endif -%} {% endif -%}
{% if user_can(permissions.DELETE_APPLICATION_MEMBER) and member.role_status == 'pending' -%} {% if user_can(permissions.DELETE_APPLICATION_MEMBER) and (invite_pending or invite_expired) -%}
{% set revoke_invite_modal = "revoke_invite_{}".format(member.role_id) %} {% set revoke_invite_modal = "revoke_invite_{}".format(member.role_id) %}
{% call Modal(name=revoke_invite_modal) %} {% call Modal(name=revoke_invite_modal) %}
<form method="post" action="{{ url_for('applications.revoke_invite', application_id=application.id, application_role_id=member.role_id) }}"> <form method="post" action="{{ url_for('applications.revoke_invite', application_id=application.id, application_role_id=member.role_id) }}">
@ -104,14 +106,13 @@
<tbody> <tbody>
{% for member in members %} {% for member in members %}
{% set perms_modal = "edit_member-{}".format(loop.index) %} {% set perms_modal = "edit_member-{}".format(loop.index) %}
{% set invite_pending = member.role_status == 'invite_pending' %}
{% set invite_expired = member.role_status == 'invite_expired' %}
<tr> <tr>
<td> <td>
<strong>{{ member.user_name }}</strong> <strong>{{ member.user_name }}</strong>
{% if member.role_status == 'pending' %} <br>
<br> {{ Label(type=member.role_status, classes='label--below') }}
{{ Label('envelope', 'invite pending', 'success', classes='label--below') }}
{% endif %}
</td> </td>
<td> <td>
@ -148,10 +149,10 @@
) )
}} }}
{% call ToggleSection(section_name=section, classes="app-member-menu__toggle") %} {% call ToggleSection(section_name=section, classes="app-member-menu__toggle") %}
<a v-on:click="openModal('{{ perms_modal }}')"> <a v-on:click="openModal('{{ perms_modal }}')">
{{ "portfolios.applications.members.menu.edit" | translate }} {{ "portfolios.applications.members.menu.edit" | translate }}
</a> </a>
{% if member.role_status == 'pending' -%} {% if invite_pending or invite_expired -%}
{% set revoke_invite_modal = "revoke_invite_{}".format(member.role_id) %} {% set revoke_invite_modal = "revoke_invite_{}".format(member.role_id) %}
{% set resend_invite_modal = "resend_invite-{}".format(member.role_id) %} {% set resend_invite_modal = "resend_invite-{}".format(member.role_id) %}
<a v-on:click='openModal("{{ resend_invite_modal }}")'> <a v-on:click='openModal("{{ resend_invite_modal }}")'>

View File

@ -1,8 +1,19 @@
{% from "components/icon.html" import Icon %} {% from "components/icon.html" import Icon %}
{% macro Label(icon_name, text, type=None, classes="") -%} {% macro Label(type=None, classes="") -%}
{% set label_info = {
"invite_pending": {"icon": "envelope", "text": "invite pending", "color": "success",},
"invite_expired": {"icon": "envelope", "text": "invite expired", "color": "error",},
"changes_pending": {
"icon": "exchange",
"text": "changes pending",
"color": "default",
},
} %}
<span class='label {% if type %}label--{{ type }}{% endif %} {{ classes }}'> {% if type -%}
{{ Icon(icon_name) }} {{ text }} <span class='label label--{{ label_info[type]["color"] }} {{ classes }}'>
</span> {{ Icon(label_info[type]["icon"]) }} {{ label_info[type]["text"] }}
</span>
{%- endif %}
{%- endmacro %} {%- endmacro %}

View File

@ -233,6 +233,16 @@ class ApplicationRoleFactory(Base):
status = ApplicationRoleStatus.PENDING status = ApplicationRoleStatus.PENDING
permission_sets = factory.LazyFunction(base_application_permission_sets) permission_sets = factory.LazyFunction(base_application_permission_sets)
@classmethod
def _create(cls, model_class, *args, **kwargs):
with_invite = kwargs.pop("invite", True)
app_role = super()._create(model_class, *args, **kwargs)
if with_invite:
ApplicationInvitationFactory.create(role=app_role)
return app_role
class EnvironmentRoleFactory(Base): class EnvironmentRoleFactory(Base):
class Meta: class Meta:
@ -259,7 +269,7 @@ class ApplicationInvitationFactory(Base):
email = factory.Faker("email") email = factory.Faker("email")
status = InvitationStatus.PENDING status = InvitationStatus.PENDING
expiration_time = PortfolioInvitations.current_expiration_time() expiration_time = PortfolioInvitations.current_expiration_time()
role = factory.SubFactory(ApplicationRoleFactory) role = factory.SubFactory(ApplicationRoleFactory, invite=False)
class AttachmentFactory(Base): class AttachmentFactory(Base):

View File

@ -58,3 +58,21 @@ def test_environment_roles():
) )
assert not EnvironmentRoles.get_by_user_and_environment(user.id, environment2.id) assert not EnvironmentRoles.get_by_user_and_environment(user.id, environment2.id)
def test_display_status():
yesterday = datetime.date.today() - datetime.timedelta(days=1)
expired_invite = ApplicationInvitationFactory.create(expiration_time=yesterday)
assert expired_invite.role.display_status == "invite_expired"
app_role_pending = ApplicationRoleFactory.create()
invite = ApplicationInvitationFactory.create(
role=app_role_pending, user=app_role_pending.user
)
assert app_role_pending.display_status == "invite_pending"
app_role_active = ApplicationRoleFactory.create(status=ApplicationRoleStatus.ACTIVE)
assert app_role_active.display_status == None
env_role_pending = EnvironmentRoleFactory.create(application_role=app_role_active)
assert env_role_pending.application_role.display_status == "changes_pending"

View File

@ -10,10 +10,11 @@ from tests.factories import *
from atst.domain.applications import Applications from atst.domain.applications import Applications
from atst.domain.application_roles import ApplicationRoles from atst.domain.application_roles import ApplicationRoles
from atst.domain.environment_roles import EnvironmentRoles from atst.domain.environment_roles import EnvironmentRoles
from atst.domain.invitations import ApplicationInvitations
from atst.domain.common import Paginator from atst.domain.common import Paginator
from atst.domain.permission_sets import PermissionSets from atst.domain.permission_sets import PermissionSets
from atst.models.application_role import Status as ApplicationRoleStatus from atst.models.application_role import Status as ApplicationRoleStatus
from atst.models.environment_role import CSPRole from atst.models.environment_role import CSPRole, EnvironmentRole
from atst.models.permissions import Permissions from atst.models.permissions import Permissions
from atst.forms.application import EditEnvironmentForm from atst.forms.application import EditEnvironmentForm
from atst.forms.application_member import UpdateMemberForm from atst.forms.application_member import UpdateMemberForm
@ -114,7 +115,6 @@ def test_edit_application_environments_obj(app, client, user_session):
application_role=app_role1, environment=env, role=CSPRole.BASIC_ACCESS.value application_role=app_role1, environment=env, role=CSPRole.BASIC_ACCESS.value
) )
app_role2 = ApplicationRoleFactory.create(application=application, user=None) app_role2 = ApplicationRoleFactory.create(application=application, user=None)
invite = ApplicationInvitationFactory.create(role=app_role2)
env_role2 = EnvironmentRoleFactory.create( env_role2 = EnvironmentRoleFactory.create(
application_role=app_role2, environment=env, role=CSPRole.NETWORK_ADMIN.value application_role=app_role2, environment=env, role=CSPRole.NETWORK_ADMIN.value
) )
@ -165,7 +165,7 @@ def test_get_members_data(app, client, user_session):
"name": "testing", "name": "testing",
"members": [{"user": user, "role_name": CSPRole.BASIC_ACCESS.value}], "members": [{"user": user, "role_name": CSPRole.BASIC_ACCESS.value}],
} }
] ],
) )
environment = application.environments[0] environment = application.environments[0]
app_role = ApplicationRoles.get(user_id=user.id, application_id=application.id) app_role = ApplicationRoles.get(user_id=user.id, application_id=application.id)