Merge pull request #788 from dod-ccpo/edit-env-form-view
Edit env form view
This commit is contained in:
commit
ac36e34c13
@ -7,45 +7,53 @@ from atst.domain.applications import Applications
|
|||||||
from atst.forms.app_settings import EnvironmentRolesForm
|
from atst.forms.app_settings import EnvironmentRolesForm
|
||||||
from atst.forms.application import ApplicationForm, EditEnvironmentForm
|
from atst.forms.application import ApplicationForm, EditEnvironmentForm
|
||||||
from atst.domain.authz.decorator import user_can_access_decorator as user_can
|
from atst.domain.authz.decorator import user_can_access_decorator as user_can
|
||||||
|
from atst.models.environment_role import CSPRole
|
||||||
from atst.domain.exceptions import NotFoundError
|
from atst.domain.exceptions import NotFoundError
|
||||||
|
|
||||||
from atst.models.permissions import Permissions
|
from atst.models.permissions import Permissions
|
||||||
from atst.utils.flash import formatted_flash as flash
|
from atst.utils.flash import formatted_flash as flash
|
||||||
|
|
||||||
|
|
||||||
def get_environments_obj_for_app(application):
|
def get_environments_obj_for_app(application):
|
||||||
environments_obj = {}
|
environments_obj = []
|
||||||
|
|
||||||
for env in application.environments:
|
for env in application.environments:
|
||||||
environments_obj[env.name] = {
|
env_data = {
|
||||||
"edit_form": EditEnvironmentForm(obj=env),
|
|
||||||
"id": env.id,
|
"id": env.id,
|
||||||
"members": [],
|
"name": env.name,
|
||||||
|
"edit_form": EditEnvironmentForm(obj=env),
|
||||||
|
"members_form": EnvironmentRolesForm(data=data_for_env_members_form(env)),
|
||||||
|
"members": sort_env_users_by_role(env),
|
||||||
}
|
}
|
||||||
for user in env.users:
|
environments_obj.append(env_data)
|
||||||
env_role = EnvironmentRoles.get(user.id, env.id)
|
|
||||||
environments_obj[env.name]["members"].append(
|
|
||||||
{"name": user.full_name, "role": env_role.displayname}
|
|
||||||
)
|
|
||||||
|
|
||||||
return environments_obj
|
return environments_obj
|
||||||
|
|
||||||
|
|
||||||
def serialize_env_member_form_data(application):
|
def sort_env_users_by_role(env):
|
||||||
environments_list = []
|
users_dict = {"no_access": []}
|
||||||
for env in application.environments:
|
for role in CSPRole:
|
||||||
env_info = {"env_id": env.id, "team_roles": []}
|
users_dict[role.value] = []
|
||||||
for user in env.users:
|
|
||||||
env_role = EnvironmentRoles.get(user.id, env.id)
|
for user in env.application.users:
|
||||||
env_info["team_roles"].append(
|
if user in env.users:
|
||||||
{
|
role = EnvironmentRoles.get(user.id, env.id)
|
||||||
"name": user.full_name,
|
users_dict[role.displayname].append(
|
||||||
"user_id": user.id,
|
{"name": user.full_name, "user_id": user.id}
|
||||||
"role": env_role.displayname,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
environments_list.append(env_info)
|
else:
|
||||||
return environments_list
|
users_dict["no_access"].append({"name": user.full_name, "user_id": user.id})
|
||||||
|
|
||||||
|
return users_dict
|
||||||
|
|
||||||
|
|
||||||
|
def data_for_env_members_form(environment):
|
||||||
|
data = {"env_id": environment.id, "team_roles": []}
|
||||||
|
for user in environment.users:
|
||||||
|
env_role = EnvironmentRoles.get(user.id, environment.id)
|
||||||
|
data["team_roles"].append(
|
||||||
|
{"name": user.full_name, "user_id": user.id, "role": env_role.displayname}
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def check_users_are_in_application(user_ids, application):
|
def check_users_are_in_application(user_ids, application):
|
||||||
@ -62,18 +70,12 @@ def settings(application_id):
|
|||||||
# refactor like portfolio admin render function
|
# refactor like portfolio admin render function
|
||||||
application = Applications.get(application_id)
|
application = Applications.get(application_id)
|
||||||
form = ApplicationForm(name=application.name, description=application.description)
|
form = ApplicationForm(name=application.name, description=application.description)
|
||||||
app_envs_data = serialize_env_member_form_data(application)
|
|
||||||
|
|
||||||
env_forms = {}
|
|
||||||
for env_data in app_envs_data:
|
|
||||||
env_forms[env_data["env_id"]] = EnvironmentRolesForm(data=env_data)
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"portfolios/applications/settings.html",
|
"portfolios/applications/settings.html",
|
||||||
application=application,
|
application=application,
|
||||||
form=form,
|
form=form,
|
||||||
environments_obj=get_environments_obj_for_app(application=application),
|
environments_obj=get_environments_obj_for_app(application=application),
|
||||||
env_forms=env_forms,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -116,17 +118,11 @@ def update(application_id):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
env_data = serialize_env_member_form_data(application)
|
|
||||||
env_forms = {}
|
|
||||||
for data in env_data:
|
|
||||||
env_forms[data["env_id"]] = EnvironmentRolesForm(data=data)
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"portfolios/applications/settings.html",
|
"portfolios/applications/settings.html",
|
||||||
application=application,
|
application=application,
|
||||||
form=form,
|
form=form,
|
||||||
environments_obj=get_environments_obj_for_app(application=application),
|
environments_obj=get_environments_obj_for_app(application=application),
|
||||||
env_forms=env_forms,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -167,7 +163,6 @@ def update_env_roles(environment_id):
|
|||||||
name=application.name, description=application.description
|
name=application.name, description=application.description
|
||||||
),
|
),
|
||||||
environments_obj=get_environments_obj_for_app(application=application),
|
environments_obj=get_environments_obj_for_app(application=application),
|
||||||
env_forms=env_roles_form,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,3 +17,45 @@
|
|||||||
margin-right: -$gap;
|
margin-right: -$gap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-team-settings-link {
|
||||||
|
font-size: $small-font-size;
|
||||||
|
font-weight: $font-normal;
|
||||||
|
padding-left: $gap * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-role {
|
||||||
|
padding: $gap * 3;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: $gap / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-role__users {
|
||||||
|
background-color: $color-gray-lightest;
|
||||||
|
padding: ($gap * 1.2) ($gap * 0.6);
|
||||||
|
font-size: $small-font-size;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.environment-role__user {
|
||||||
|
background-color: $color-white;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: ($gap / 2) $gap;
|
||||||
|
border: solid 2px $color-blue;
|
||||||
|
margin: $gap;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 20rem;
|
||||||
|
|
||||||
|
&.unassigned {
|
||||||
|
border: solid 1px $color-gray-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-role__no-user {
|
||||||
|
margin: $gap;
|
||||||
|
padding: ($gap / 2) $gap;
|
||||||
|
font-weight: $font-normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,28 @@
|
|||||||
{% from "components/text_input.html" import TextInput %}
|
{% from "components/text_input.html" import TextInput %}
|
||||||
{% from "components/save_button.html" import SaveButton %}
|
{% from "components/save_button.html" import SaveButton %}
|
||||||
|
|
||||||
|
{% macro RolePanel(users=[], role='no_access') %}
|
||||||
|
{% if role == 'no_access' %}
|
||||||
|
{% set role = 'Unassigned (No Access)' %}
|
||||||
|
{% set unassigned = True %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class='environment-role'>
|
||||||
|
<h4>{{ role }}</h4>
|
||||||
|
<ul class='environment-role__users'>
|
||||||
|
{% for user in users %}
|
||||||
|
<li class="environment-role__user {{ 'unassigned' if unassigned }}">
|
||||||
|
{{ user.name }}{{ Icon('edit', classes="icon--medium right") }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if users == [] %}
|
||||||
|
<div class='environment-role__no-user'>Currently no members are in this role</div>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
<div class="application-list-item">
|
<div class="application-list-item">
|
||||||
<header>
|
<header>
|
||||||
<div class="responsive-table-wrapper__header">
|
<div class="responsive-table-wrapper__header">
|
||||||
@ -22,12 +44,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="accordion-table__items">
|
<ul class="accordion-table__items">
|
||||||
{% for name, environment_info in environments_obj.items() %}
|
{% for env in environments_obj %}
|
||||||
|
{% set member_count = env['members_form'].data['team_roles'] | length %}
|
||||||
|
{% set members_by_role = env['members'] %}
|
||||||
|
{% set unassigned = members_by_role['no_access'] %}
|
||||||
|
|
||||||
<toggler inline-template>
|
<toggler inline-template>
|
||||||
<li class="accordion-table__item">
|
<li class="accordion-table__item">
|
||||||
<div class="accordion-table__item-content">
|
<div class="accordion-table__item-content">
|
||||||
<span>
|
<span>
|
||||||
{{ name }}
|
{{ env['name'] }}
|
||||||
</span>
|
</span>
|
||||||
<span class="icon-link">
|
<span class="icon-link">
|
||||||
{% set edit_environment_button %}
|
{% set edit_environment_button %}
|
||||||
@ -44,11 +70,11 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="icon-link icon-link--large accordion-table__item__toggler">
|
<span class="icon-link icon-link--large accordion-table__item__toggler">
|
||||||
{% set open_members_button %}
|
{% set open_members_button %}
|
||||||
{{ "common.members" | translate }} ({{ members_list | length }}) {{ Icon('caret_down') }}
|
{{ "common.members" | translate }} ({{ member_count }}) {{ Icon('caret_down') }}
|
||||||
{% endset %}
|
{% endset %}
|
||||||
|
|
||||||
{% set close_members_button %}
|
{% set close_members_button %}
|
||||||
{{ "common.members" | translate }} ({{ members_list | length }}) {{ Icon('caret_up') }}
|
{{ "common.members" | translate }} ({{ member_count }}) {{ Icon('caret_up') }}
|
||||||
{% endset %}
|
{% endset %}
|
||||||
|
|
||||||
{{
|
{{
|
||||||
@ -62,21 +88,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% call ToggleSection(section_name="members") %}
|
{% call ToggleSection(section_name="members") %}
|
||||||
<ul>
|
<div class='app-team-settings-link'>Need to add someone new to the team? <a href='{{ url_for("applications.team", application_id=application.id) }}'>Jump to Team Settings</a></div>
|
||||||
{% for member in environment_info['members'] %}
|
{% for role, members in members_by_role.items() %}
|
||||||
<li class="accordion-table__item__expanded">
|
{{ RolePanel(users=members, role=role) }}
|
||||||
<div class="accordion-table__item__expanded_first">{{ member.name }}</div>
|
|
||||||
<div class="right">{{ member.role }}</div>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
{% call ToggleSection(section_name="edit") %}
|
{% call ToggleSection(section_name="edit") %}
|
||||||
<ul>
|
<ul>
|
||||||
<li class="accordion-table__item__expanded">
|
<li class="accordion-table__item__expanded">
|
||||||
{% set edit_form = environment_info['edit_form'] %}
|
{% set edit_form = env['edit_form'] %}
|
||||||
<form action="{{ url_for('applications.update_environment', environment_id=environment_info['id']) }}" method="post">
|
<form action="{{ url_for('applications.update_environment', environment_id=env['id']) }}" method="post">
|
||||||
{{ edit_form.csrf_token }}
|
{{ edit_form.csrf_token }}
|
||||||
{{ TextInput(edit_form.name) }}
|
{{ TextInput(edit_form.name) }}
|
||||||
{{
|
{{
|
||||||
|
@ -16,21 +16,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="accordion-table__items">
|
<ul class="accordion-table__items">
|
||||||
{% for name, members_list in environments_obj.items() %}
|
{% for env in environments_obj %}
|
||||||
<toggler inline-template>
|
<toggler inline-template>
|
||||||
<li class="accordion-table__item">
|
<li class="accordion-table__item">
|
||||||
<div class="accordion-table__item-content">
|
<div class="accordion-table__item-content">
|
||||||
<span>
|
<span>
|
||||||
{{ name }}
|
{{ env['name'] }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="icon-link icon-link--large accordion-table__item__toggler">
|
<span class="icon-link icon-link--large accordion-table__item__toggler">
|
||||||
{% set open_members_button %}
|
{% set open_members_button %}
|
||||||
{{ "common.members" | translate }} ({{ members_list | length }}) {{ Icon('caret_down') }}
|
{{ "common.members" | translate }} ({{ env['members'] | length }}) {{ Icon('caret_down') }}
|
||||||
{% endset %}
|
{% endset %}
|
||||||
|
|
||||||
{% set close_members_button %}
|
{% set close_members_button %}
|
||||||
{{ "common.members" | translate }} ({{ members_list | length }}) {{ Icon('caret_up') }}
|
{{ "common.members" | translate }} ({{ env['members'] | length }}) {{ Icon('caret_up') }}
|
||||||
{% endset %}
|
{% endset %}
|
||||||
|
|
||||||
{{
|
{{
|
||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
{% call ToggleSection(section_name="members") %}
|
{% call ToggleSection(section_name="members") %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for member in members_list %}
|
{% for member in env['members'] %}
|
||||||
<li class="accordion-table__item__expanded">
|
<li class="accordion-table__item__expanded">
|
||||||
<div class="accordion-table__item__expanded_first">{{ member.name }}</div>
|
<div class="accordion-table__item__expanded_first">{{ member.name }}</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -22,6 +22,7 @@ from atst.domain.exceptions import NotFoundError
|
|||||||
from atst.models.environment_role import CSPRole
|
from atst.models.environment_role import CSPRole
|
||||||
from atst.models.portfolio_role import Status as PortfolioRoleStatus
|
from atst.models.portfolio_role import Status as PortfolioRoleStatus
|
||||||
from atst.forms.application import EditEnvironmentForm
|
from atst.forms.application import EditEnvironmentForm
|
||||||
|
from atst.forms.app_settings import EnvironmentRolesForm
|
||||||
|
|
||||||
from tests.utils import captured_templates
|
from tests.utils import captured_templates
|
||||||
|
|
||||||
@ -72,15 +73,18 @@ def test_edit_application_environments_obj(app, client, user_session):
|
|||||||
portfolio,
|
portfolio,
|
||||||
"Snazzy Application",
|
"Snazzy Application",
|
||||||
"A new application for me and my friends",
|
"A new application for me and my friends",
|
||||||
{"env1", "env2"},
|
{"env"},
|
||||||
)
|
)
|
||||||
user1 = UserFactory.create()
|
env = application.environments[0]
|
||||||
user2 = UserFactory.create()
|
app_role = ApplicationRoleFactory.create(application=application)
|
||||||
env1 = application.environments[0]
|
env_role1 = EnvironmentRoleFactory.create(
|
||||||
env2 = application.environments[1]
|
environment=env, role=CSPRole.BASIC_ACCESS.value
|
||||||
env_role1 = EnvironmentRoleFactory.create(environment=env1, user=user1)
|
)
|
||||||
env_role2 = EnvironmentRoleFactory.create(environment=env1, user=user2)
|
ApplicationRoleFactory.create(application=application, user=env_role1.user)
|
||||||
env_role3 = EnvironmentRoleFactory.create(environment=env2, user=user1)
|
env_role2 = EnvironmentRoleFactory.create(
|
||||||
|
environment=env, role=CSPRole.NETWORK_ADMIN.value
|
||||||
|
)
|
||||||
|
ApplicationRoleFactory.create(application=application, user=env_role2.user)
|
||||||
|
|
||||||
user_session(portfolio.owner)
|
user_session(portfolio.owner)
|
||||||
|
|
||||||
@ -92,54 +96,24 @@ def test_edit_application_environments_obj(app, client, user_session):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
_, context = templates[0]
|
_, context = templates[0]
|
||||||
|
|
||||||
env_obj_1 = context["environments_obj"][env1.name]
|
env_obj = context["environments_obj"][0]
|
||||||
assert env_obj_1["id"] == env1.id
|
assert env_obj["name"] == env.name
|
||||||
assert isinstance(env_obj_1["edit_form"], EditEnvironmentForm)
|
assert env_obj["id"] == env.id
|
||||||
assert env_obj_1["members"] == [
|
assert isinstance(env_obj["edit_form"], EditEnvironmentForm)
|
||||||
{"name": user1.full_name, "role": env_role1.role},
|
assert isinstance(env_obj["members_form"], EnvironmentRolesForm)
|
||||||
{"name": user2.full_name, "role": env_role2.role},
|
assert env_obj["members"] == {
|
||||||
]
|
"no_access": [
|
||||||
|
{"name": app_role.user.full_name, "user_id": app_role.user_id}
|
||||||
|
],
|
||||||
def test_edit_app_serialize_env_member_form_data(app, client, user_session):
|
CSPRole.BASIC_ACCESS.value: [
|
||||||
portfolio = PortfolioFactory.create()
|
{"name": env_role1.user.full_name, "user_id": env_role1.user_id}
|
||||||
application = Applications.create(
|
],
|
||||||
portfolio,
|
CSPRole.NETWORK_ADMIN.value: [
|
||||||
"Snazzy Application",
|
{"name": env_role2.user.full_name, "user_id": env_role2.user_id}
|
||||||
"A new application for me and my friends",
|
],
|
||||||
{"env1", "env2"},
|
CSPRole.BUSINESS_READ.value: [],
|
||||||
)
|
CSPRole.TECHNICAL_READ.value: [],
|
||||||
user1 = UserFactory.create()
|
|
||||||
user2 = UserFactory.create()
|
|
||||||
env1 = application.environments[0]
|
|
||||||
env2 = application.environments[1]
|
|
||||||
env_role1 = EnvironmentRoleFactory.create(environment=env1, user=user1)
|
|
||||||
env_role2 = EnvironmentRoleFactory.create(environment=env1, user=user2)
|
|
||||||
env_role3 = EnvironmentRoleFactory.create(environment=env2, user=user1)
|
|
||||||
|
|
||||||
user_session(portfolio.owner)
|
|
||||||
|
|
||||||
with captured_templates(app) as templates:
|
|
||||||
response = app.test_client().get(
|
|
||||||
url_for("applications.settings", application_id=application.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
_, context = templates[0]
|
|
||||||
for env_id in context["env_forms"]:
|
|
||||||
env = Environments.get(environment_id=env_id)
|
|
||||||
form_data = {"env_id": env_id, "team_roles": []}
|
|
||||||
for user in env.users:
|
|
||||||
env_role = EnvironmentRoles.get(user.id, env.id)
|
|
||||||
form_data["team_roles"].append(
|
|
||||||
{
|
|
||||||
"name": user.full_name,
|
|
||||||
"user_id": user.id,
|
|
||||||
"role": env_role.displayname,
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
assert context["env_forms"][env_id].data == form_data
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_with_permission_can_update_application(client, user_session):
|
def test_user_with_permission_can_update_application(client, user_session):
|
||||||
|
@ -16,6 +16,7 @@ from tests.factories import (
|
|||||||
ApplicationFactory,
|
ApplicationFactory,
|
||||||
ApplicationRoleFactory,
|
ApplicationRoleFactory,
|
||||||
EnvironmentFactory,
|
EnvironmentFactory,
|
||||||
|
EnvironmentRoleFactory,
|
||||||
InvitationFactory,
|
InvitationFactory,
|
||||||
PortfolioFactory,
|
PortfolioFactory,
|
||||||
PortfolioRoleFactory,
|
PortfolioRoleFactory,
|
||||||
@ -264,6 +265,11 @@ def test_application_settings_access(get_url_assert_status):
|
|||||||
applications=[{"name": "Mos Eisley", "description": "Where Han shot first"}],
|
applications=[{"name": "Mos Eisley", "description": "Where Han shot first"}],
|
||||||
)
|
)
|
||||||
app = portfolio.applications[0]
|
app = portfolio.applications[0]
|
||||||
|
env = EnvironmentFactory.create(application=app)
|
||||||
|
env_role = EnvironmentRoleFactory.create(
|
||||||
|
environment=env, role=CSPRole.NETWORK_ADMIN.value
|
||||||
|
)
|
||||||
|
ApplicationRoleFactory.create(application=app, user=env_role.user)
|
||||||
|
|
||||||
url = url_for("applications.settings", application_id=app.id)
|
url = url_for("applications.settings", application_id=app.id)
|
||||||
get_url_assert_status(ccpo, url, 200)
|
get_url_assert_status(ccpo, url, 200)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user