Update application environments
This commit is contained in:
parent
30018a061b
commit
079672c818
@ -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:
|
||||||
|
@ -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()]
|
||||||
|
@ -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):
|
||||||
|
@ -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 }) }}""",
|
||||||
|
@ -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,
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.accordion-table {
|
.accordion-table {
|
||||||
|
.usa-alert {
|
||||||
|
margin: $gap*2;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
@ -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 %}
|
||||||
|
@ -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>
|
||||||
|
@ -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"
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user