Update application environments

This commit is contained in:
George Drummond 2019-04-22 10:37:03 -04:00
parent 30018a061b
commit 079672c818
No known key found for this signature in database
GPG Key ID: 296DD6077123BF17
12 changed files with 159 additions and 35 deletions

View File

@ -49,6 +49,13 @@ class Environments(object):
.all() .all()
) )
@classmethod
def update(cls, environment, name=None):
if name is not None:
environment.name = name
db.session.add(environment)
db.session.commit()
@classmethod @classmethod
def get(cls, environment_id): def get(cls, environment_id):
try: try:

View File

@ -5,6 +5,12 @@ from atst.forms.validators import ListItemRequired, ListItemsUnique
from atst.utils.localization import translate from atst.utils.localization import translate
class EditEnvironmentForm(BaseForm):
name = StringField(
label=translate("forms.environments.name_label"), validators=[Required()]
)
class ApplicationForm(BaseForm): class ApplicationForm(BaseForm):
name = StringField( name = StringField(
label=translate("forms.application.name_label"), validators=[Required()] label=translate("forms.application.name_label"), validators=[Required()]

View File

@ -4,8 +4,8 @@ from . import applications_bp
from atst.domain.environment_roles import EnvironmentRoles from atst.domain.environment_roles import EnvironmentRoles
from atst.domain.environments import Environments from atst.domain.environments import Environments
from atst.domain.applications import Applications from atst.domain.applications import Applications
from atst.forms.application import ApplicationForm
from atst.forms.app_settings import EnvironmentRolesForm from atst.forms.app_settings import EnvironmentRolesForm
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.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
@ -15,10 +15,14 @@ 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] = [] environments_obj[env.name] = {
"edit_form": EditEnvironmentForm(obj=env),
"id": env.id,
"members": [],
}
for user in env.users: for user in env.users:
env_role = EnvironmentRoles.get(user.id, env.id) env_role = EnvironmentRoles.get(user.id, env.id)
environments_obj[env.name].append( environments_obj[env.name]["members"].append(
{"name": user.full_name, "role": env_role.displayname} {"name": user.full_name, "role": env_role.displayname}
) )
@ -49,6 +53,7 @@ def settings(application_id):
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) app_envs_data = serialize_env_member_form_data(application)
env_forms = {} env_forms = {}
for env_data in app_envs_data: for env_data in app_envs_data:
env_forms[env_data["env_id"]] = EnvironmentRolesForm(data=env_data) env_forms[env_data["env_id"]] = EnvironmentRolesForm(data=env_data)
@ -62,6 +67,29 @@ def settings(application_id):
) )
@applications_bp.route("/environments/<environment_id>/edit", methods=["POST"])
@user_can(Permissions.EDIT_ENVIRONMENT, message="edit application environments")
def update_environment(environment_id):
environment = Environments.get(environment_id)
application = environment.application
form = EditEnvironmentForm(formdata=http_request.form)
if form.validate():
Environments.update(environment=environment, name=form.name.data)
flash("application_environments_updated")
return redirect(
url_for(
"applications.settings",
application_id=application.id,
fragment="application-environments",
_anchor="application-environments",
)
)
@applications_bp.route("/applications/<application_id>/edit", methods=["POST"]) @applications_bp.route("/applications/<application_id>/edit", methods=["POST"])
@user_can(Permissions.EDIT_APPLICATION, message="update application") @user_can(Permissions.EDIT_APPLICATION, message="update application")
def update(application_id): def update(application_id):

View File

@ -2,6 +2,11 @@ from flask import flash, render_template_string
from atst.utils.localization import translate from atst.utils.localization import translate
MESSAGES = { MESSAGES = {
"application_environments_updated": {
"title_template": "Application environments updated",
"message_template": "Application environments have been updated",
"category": "success",
},
"primary_point_of_contact_changed": { "primary_point_of_contact_changed": {
"title_template": translate("flash.new_ppoc_title"), "title_template": translate("flash.new_ppoc_title"),
"message_template": """{{ "flash.new_ppoc_message" | translate({ "ppoc_name": ppoc_name }) }}""", "message_template": """{{ "flash.new_ppoc_message" | translate({ "ppoc_name": ppoc_name }) }}""",

View File

@ -1,6 +1,15 @@
import FormMixin from '../mixins/form'
import textinput from './text_input'
export default { export default {
name: 'toggler', name: 'toggler',
mixins: [FormMixin],
components: {
textinput,
},
data: function() { data: function() {
return { return {
selectedSection: null, selectedSection: null,

View File

@ -3,6 +3,11 @@
} }
.accordion-table { .accordion-table {
.usa-alert {
margin: $gap*2;
margin-bottom: 0;
}
ul { ul {
padding-left: 0; padding-left: 0;
} }

View File

@ -1,5 +1,7 @@
{% from "components/icon.html" import Icon %} {% from "components/icon.html" import Icon %}
{% from "components/toggle_list.html" import ToggleButton, ToggleSection %} {% from "components/toggle_list.html" import ToggleButton, ToggleSection %}
{% from "components/text_input.html" import TextInput %}
{% from "components/save_button.html" import SaveButton %}
<div class="application-list-item"> <div class="application-list-item">
<header> <header>
@ -20,7 +22,7 @@
</div> </div>
<ul class="accordion-table__items"> <ul class="accordion-table__items">
{% for name, members_list in environments_obj.items() %} {% for name, environment_info in environments_obj.items() %}
<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">
@ -61,7 +63,7 @@
{% call ToggleSection(section_name="members") %} {% call ToggleSection(section_name="members") %}
<ul> <ul>
{% for member in members_list %} {% for member in environment_info['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>
<div class="right">{{ member.role }}</div> <div class="right">{{ member.role }}</div>
@ -73,15 +75,16 @@
{% call ToggleSection(section_name="edit") %} {% call ToggleSection(section_name="edit") %}
<ul> <ul>
<li class="accordion-table__item__expanded"> <li class="accordion-table__item__expanded">
<div> {% set edit_form = environment_info['edit_form'] %}
<form> <form action="{{ url_for('applications.update_environment', environment_id=environment_info['id']) }}" method="post">
<div class="form-row"> {{ edit_form.csrf_token }}
<div class="form-col form-col--half"> {{ TextInput(edit_form.name) }}
Row here {{
</div> SaveButton(
</div> text=("portfolios.applications.update_button_text" | translate)
)
}}
</form> </form>
</div>
</li> </li>
</ul> </ul>
{% endcall %} {% endcall %}

View File

@ -77,7 +77,12 @@
</div> </div>
</form> </form>
<div id="application-environments">
<div class="accordion-table responsive-table-wrapper panel"> <div class="accordion-table responsive-table-wrapper panel">
{% if g.matchesPath("application-environments") %}
{% include "fragments/flash.html" %}
{% endif %}
{% if user_can(permissions.EDIT_APPLICATION) %} {% if user_can(permissions.EDIT_APPLICATION) %}
{% include "fragments/applications/edit_environments.html" %} {% include "fragments/applications/edit_environments.html" %}
<div class="panel__footer"> <div class="panel__footer">
@ -92,8 +97,9 @@
{% elif user_can(permissions.VIEW_ENVIRONMENT) %} {% elif user_can(permissions.VIEW_ENVIRONMENT) %}
{% include "fragments/applications/read_only_environments.html" %} {% include "fragments/applications/read_only_environments.html" %}
{% endif %} {% endif %}
</div> </div>
</div>
{% if user_can(permissions.DELETE_APPLICATION) %} {% if user_can(permissions.DELETE_APPLICATION) %}
{% call Modal(name="delete-application") %} {% call Modal(name="delete-application") %}
<h1>{{ "portfolios.applications.delete.header" | translate }}</h1> <h1>{{ "portfolios.applications.delete.header" | translate }}</h1>

View File

@ -187,3 +187,10 @@ def test_delete_environment(session):
assert env_role.deleted assert env_role.deleted
# flushed the change # flushed the change
assert not session.dirty assert not session.dirty
def test_update_environment():
environment = EnvironmentFactory.create()
assert environment.name is not "name 2"
Environments.update(environment, name="name 2")
assert environment.name == "name 2"

View File

@ -17,10 +17,36 @@ from atst.domain.permission_sets import PermissionSets
from atst.domain.portfolios import Portfolios from atst.domain.portfolios import Portfolios
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 tests.utils import captured_templates from tests.utils import captured_templates
def test_updating_application_environments(client, user_session):
portfolio = PortfolioFactory.create()
application = ApplicationFactory.create(portfolio=portfolio)
environment = EnvironmentFactory.create(application=application)
user_session(portfolio.owner)
form_data = {"name": "new name a"}
response = client.post(
url_for("applications.update_environment", environment_id=environment.id),
data=form_data,
)
assert response.status_code == 302
assert response.location == url_for(
"applications.settings",
application_id=application.id,
_external=True,
fragment="application-environments",
_anchor="application-environments",
)
assert environment.name == "new name a"
def test_application_settings(client, user_session): def test_application_settings(client, user_session):
portfolio = PortfolioFactory.create() portfolio = PortfolioFactory.create()
application = Applications.create( application = Applications.create(
@ -61,13 +87,14 @@ 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]
assert context["environments_obj"] == {
env1.name: [ env_obj_1 = context["environments_obj"][env1.name]
assert env_obj_1["id"] == env1.id
assert isinstance(env_obj_1["edit_form"], EditEnvironmentForm)
assert env_obj_1["members"] == [
{"name": user1.full_name, "role": env_role1.role}, {"name": user1.full_name, "role": env_role1.role},
{"name": user2.full_name, "role": env_role2.role}, {"name": user2.full_name, "role": env_role2.role},
], ]
env2.name: [{"name": user1.full_name, "role": env_role3.role}],
}
def test_edit_app_serialize_env_member_form_data(app, client, user_session): def test_edit_app_serialize_env_member_form_data(app, client, user_session):

View File

@ -547,6 +547,25 @@ def test_applications_update_access(post_url_assert_status):
post_url_assert_status(rando, url, 404) post_url_assert_status(rando, url, 404)
# applications.update_environments
def test_applications_update_environments(post_url_assert_status):
ccpo = UserFactory.create_ccpo()
dev = UserFactory.create()
rando = UserFactory.create()
portfolio = PortfolioFactory.create(
owner=dev,
applications=[{"name": "Mos Eisley", "description": "Where Han shot first"}],
)
app = portfolio.applications[0]
environment = EnvironmentFactory.create(application=app)
url = url_for("applications.update_environment", environment_id=environment.id)
post_url_assert_status(dev, url, 302)
post_url_assert_status(ccpo, url, 302)
post_url_assert_status(rando, url, 404)
# task_orders.view_task_order # task_orders.view_task_order
def test_task_orders_view_task_order_access(get_url_assert_status): def test_task_orders_view_task_order_access(get_url_assert_status):
ccpo = user_with(PermissionSets.VIEW_PORTFOLIO_FUNDING) ccpo = user_with(PermissionSets.VIEW_PORTFOLIO_FUNDING)

View File

@ -78,6 +78,8 @@ forms:
name_label: Name name_label: Name
assign_ppoc: assign_ppoc:
dod_id: 'Select new primary point of contact:' dod_id: 'Select new primary point of contact:'
environments:
name_label: Environment Name
ccpo_review: ccpo_review:
comment_description: Provide instructions or notes for additional information that is necessary to approve the request here. The requestor may then re-submit the updated request or initiate contact outside of AT-AT if further discussion is required. <strong>This message will be shared with the person making the JEDI request.</strong>. comment_description: Provide instructions or notes for additional information that is necessary to approve the request here. The requestor may then re-submit the updated request or initiate contact outside of AT-AT if further discussion is required. <strong>This message will be shared with the person making the JEDI request.</strong>.
comment_label: Instructions or comments comment_label: Instructions or comments