From e80585b3b2e706d883bbb8491022eff9ecc502df Mon Sep 17 00:00:00 2001 From: graham-dds Date: Tue, 24 Sep 2019 16:37:12 -0400 Subject: [PATCH 01/14] Move member editing template to macro --- templates/applications/settings.html | 150 +----------------- .../applications/fragments/members.html | 142 +++++++++++++++++ 2 files changed, 148 insertions(+), 144 deletions(-) create mode 100644 templates/portfolios/applications/fragments/members.html diff --git a/templates/applications/settings.html b/templates/applications/settings.html index 68a84965..dcf87520 100644 --- a/templates/applications/settings.html +++ b/templates/applications/settings.html @@ -5,6 +5,7 @@ {% from "components/icon.html" import Icon %} {% import "applications/fragments/new_member_modal_content.html" as member_steps %} {% from "applications/fragments/member_perms_form_fields.html" import MemberPermsFields %} +{% from "portfolios/applications/fragments/members.html" import MemberManagementTemplate %} {% from "components/modal.html" import Modal %} {% from "components/multi_step_modal_form.html" import MultiStepModalForm %} {% from "components/pagination.html" import Pagination %} @@ -73,150 +74,11 @@ {% endif %} - {% if not application.members %} - {% set user_can_invite = user_can(permissions.CREATE_APPLICATION_MEMBER) %} - -
-

{{ ("portfolios.applications.team_settings.blank_slate.title" | translate) }}

- - {{ Icon('avatar') }} - - {% if not user_can_invite %} -

{{ ("portfolios.applications.team_settings.blank_slate.sub_message" | translate) }}

- {% endif %} - - {% if user_can_invite %} - {% set new_member_modal_name = "add-app-mem" %} - - {{ "portfolios.applications.team_settings.blank_slate.action_label" | translate }} - - {{ MultiStepModalForm( - name=new_member_modal_name, - form=new_member_form, - form_action=url_for("applications.create_member", application_id=application.id), - steps=[ - member_steps.MemberStepOne(new_member_form), - member_steps.MemberStepTwo(new_member_form, application) - ], - ) }} - {% endif %} -
- - {% else %} -
- {{ 'portfolios.applications.settings.team_members' | translate }} - - {% set new_member_modal_name = "add-app-mem" %} - {% if user_can(permissions.CREATE_APPLICATION_MEMBER) %} - - {{ Icon("plus") }} - {{ "portfolios.applications.add_member" | translate }} - - {% endif %} -
- -
-
- {% if g.matchesPath("application-members") %} - {% include "fragments/flash.html" %} - {% endif %} - {% for member in members %} - {% set modal_name = "edit_member-{}".format(loop.index) %} - {% call Modal(modal_name) %} - - - - - {% endcall %} - - {% if user_can(permissions.DELETE_APPLICATION_MEMBER) and member.role_status == 'pending' %} - {% set revoke_invite_modal = "revoke_invite_{}".format(member.role_id) %} - {% call Modal(name=revoke_invite_modal, dismissable=True) %} -
-
- {{ member.form.csrf_token }} -

{{ "invites.revoke.modal_heading" | translate({'user_name': member.user_name}) }}

-
- - -
-
-
- {% endcall %} - {% endif %} - {% endfor %} - - - - - - - - - - - {% for member in members %} - {% set modal_name = "edit_member-{}".format(loop.index) %} - - - - - - - - {% endfor %} - -
MemberProject PermissionsEnvironment Access
- {{ member.user_name }} - - {{ Icon('edit') }} - -
- {% if member.role_status == 'pending' %} - INVITE PENDING - {% endif %} - -
- {% for perm, value in member.permission_sets.items() %} - {{ ("portfolios.applications.members.{}.{}".format(perm, value)) | translate }}
- {% endfor %} -
- {% for env in member.environment_roles %} - {{ env.environment_name }}{% if not env == member.environment_roles[-1]%},{% endif %} - {% endfor %} - - {% if user_can(permissions.DELETE_APPLICATION_MEMBER) and member.role_status == 'pending' %} - {% set revoke_invite_modal = "revoke_invite_{}".format(member.role_id) %} - Resend Invite
- {{ 'invites.revoke.button' | translate }} - {% endif %} -
-
- - {% if user_can(permissions.CREATE_APPLICATION_MEMBER) %} - {% import "applications/fragments/new_member_modal_content.html" as member_steps %} - {{ MultiStepModalForm( - name=new_member_modal_name, - form=new_member_form, - form_action=url_for("applications.create_member", application_id=application.id), - steps=[ - member_steps.MemberStepOne(new_member_form), - member_steps.MemberStepTwo(new_member_form, application) - ], - ) }} - {% endif %} -
- {% endif %} + {{ MemberManagementTemplate( + application, + members, + new_member_form, + user_can(permissions.CREATE_APPLICATION_MEMBER)) }}
{{ 'common.resource_names.environments' | translate }} diff --git a/templates/portfolios/applications/fragments/members.html b/templates/portfolios/applications/fragments/members.html new file mode 100644 index 00000000..9b968693 --- /dev/null +++ b/templates/portfolios/applications/fragments/members.html @@ -0,0 +1,142 @@ +{% from "components/alert.html" import Alert %} +{% from "components/icon.html" import Icon %} +{% import "fragments/applications/new_member_modal_content.html" as member_steps %} +{% from "fragments/applications/member_perms_form_fields.html" import MemberPermsFields %} +{% from "components/modal.html" import Modal %} +{% from "components/multi_step_modal_form.html" import MultiStepModalForm %} +{% from "components/save_button.html" import SaveButton %} + +{% macro MemberManagementTemplate( + application, + members, + new_member_form, + user_can_create_app_member=False +) %} + + {% if not application.members %} +
+

{{ ("portfolios.applications.team_settings.blank_slate.title" | translate) }}

+ + {{ Icon('avatar') }} + + {% if not user_can_create_app_member %} +

{{ ("portfolios.applications.team_settings.blank_slate.sub_message" | translate) }}

+ {% endif %} + + {% if user_can_create_app_member %} + {% set new_member_modal_name = "add-app-mem" %} + + {{ "portfolios.applications.team_settings.blank_slate.action_label" | translate }} + + {{ MultiStepModalForm( + name=new_member_modal_name, + form=new_member_form, + form_action=url_for("applications.create_member", application_id=application.id), + steps=[ + member_steps.MemberStepOne(new_member_form), + member_steps.MemberStepTwo(new_member_form, application) + ], + ) }} + {% endif %} +
+ + {% else %} +
+ {{ 'portfolios.applications.settings.team_members' | translate }} + + {% set new_member_modal_name = "add-app-mem" %} + {% if user_can_create_app_member %} + + {{ Icon("plus") }} + {{ "portfolios.applications.add_member" | translate }} + + {% endif %} +
+ +
+
+ {% if g.matchesPath("application-members") %} + {% include "fragments/flash.html" %} + {% endif %} + {% for member in members %} + {% set modal_name = "edit_member-{}".format(loop.index) %} + {% call Modal(modal_name) %} + + + + + {% endcall %} + {% endfor %} + + + + + + + + + + + {% for member in members %} + {% set modal_name = "edit_member-{}".format(loop.index) %} + + + + + + + + {% endfor %} + +
MemberProject PermissionsEnvironment Access
+ {{ member.user_name }} + + {{ Icon('edit') }} + +
+ {% if member.role_status == 'pending' %} + INVITE PENDING + {% endif %} + +
+ {% for perm, value in member.permission_sets.items() %} + {{ ("portfolios.applications.members.{}.{}".format(perm, value)) | translate }}
+ {% endfor %} +
+ {% for env in member.environment_roles %} + {{ env.environment_name }}{% if not env == member.environment_roles[-1]%},{% endif %} + {% endfor %} + + {% if member.role_status == 'pending' %} + Resend Invite
+ Revoke Invite + {% endif %} +
+
+ + {% if user_can_create_app_member %} + {% import "fragments/applications/new_member_modal_content.html" as member_steps %} + {{ MultiStepModalForm( + name=new_member_modal_name, + form=new_member_form, + form_action=url_for("applications.create_member", application_id=application.id), + steps=[ + member_steps.MemberStepOne(new_member_form), + member_steps.MemberStepTwo(new_member_form, application) + ], + ) }} + {% endif %} +
+ {% endif %} + +{% endmacro %} From fb58bc1ba1c5e5e78c5e1fbf61a5bb74ceebada4 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 25 Sep 2019 10:15:43 -0400 Subject: [PATCH 02/14] Add GET route for step 3 --- atst/routes/applications/new.py | 17 +++++++++++++++++ .../portfolios/applications/new/step_3.html | 12 ++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 templates/portfolios/applications/new/step_3.html diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index 2f3ce510..d3911b84 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -7,6 +7,7 @@ from atst.forms.application import NameAndDescriptionForm, EnvironmentsForm from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.models.permissions import Permissions from atst.utils.flash import formatted_flash as flash +from atst.routes.applications.settings import get_members_data, get_new_member_form def get_new_application_form(form_data, form_class, application_id=None): @@ -130,3 +131,19 @@ def update_new_application_step_2(portfolio_id, application_id): ), 400, ) + + +@applications_bp.route("/applications//step_3") +@user_can(Permissions.CREATE_APPLICATION, message="view create new application form") +def view_new_application_step_3(application_id): + application = Applications.get(application_id) + members = get_members_data(application) + new_member_form = get_new_member_form(application) + + return render_template( + "portfolios/applications/new/step_3.html", + application_id=application_id, + application=application, + members=members, + new_member_form=new_member_form, + ) diff --git a/templates/portfolios/applications/new/step_3.html b/templates/portfolios/applications/new/step_3.html new file mode 100644 index 00000000..54878b08 --- /dev/null +++ b/templates/portfolios/applications/new/step_3.html @@ -0,0 +1,12 @@ +{% extends "portfolios/applications/base.html" %} + +{% from "portfolios/applications/fragments/members.html" import MemberManagementTemplate %} +{% set secondary_breadcrumb = 'portfolios.applications.new_application_title' | translate %} + +{% block application_content %} + {{ MemberManagementTemplate( + application, + members, + new_member_form, + user_can(permissions.CREATE_APPLICATION_MEMBER)) }} +{% endblock %} From 4aa0afdb67f8b5837d218f21f19eed15b8d331c6 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 25 Sep 2019 15:54:22 -0400 Subject: [PATCH 03/14] New env role logic in filter_env_roles_form_data --- atst/routes/applications/settings.py | 5 +++-- tests/routes/applications/test_settings.py | 25 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index 74800b21..0cb90159 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -73,8 +73,9 @@ def filter_env_roles_form_data(member, environments): "environment_name": env.name, "role": NO_ACCESS, } - env_role = EnvironmentRoles.get_by_user_and_environment(member.user_id, env.id) - if env_role: + env_roles_set = set(env.roles).intersection(set(member.environment_roles)) + if len(env_roles_set) == 1: + (env_role,) = env_roles_set env_data["role"] = env_role.role env_roles_form_data.append(env_data) diff --git a/tests/routes/applications/test_settings.py b/tests/routes/applications/test_settings.py index 811bee36..ffe6f7a1 100644 --- a/tests/routes/applications/test_settings.py +++ b/tests/routes/applications/test_settings.py @@ -21,6 +21,7 @@ from atst.models.portfolio_role import Status as PortfolioRoleStatus from atst.forms.application import EditEnvironmentForm from atst.forms.application_member import UpdateMemberForm from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS +from atst.routes.applications.settings import filter_env_roles_form_data from tests.utils import captured_templates @@ -559,3 +560,27 @@ def test_revoke_invite(client, user_session): assert invite.is_revoked assert app_role.status == ApplicationRoleStatus.DISABLED + +def test_filter_environment_roles(): + application_role = ApplicationRoleFactory.create(user=None) + application_role2 = ApplicationRoleFactory.create( + user=None, application=application_role.application + ) + application_role3 = ApplicationRoleFactory.create( + user=None, application=application_role.application + ) + + environment = EnvironmentFactory.create(application=application_role.application) + + EnvironmentRoleFactory.create( + environment=environment, application_role=application_role + ) + EnvironmentRoleFactory.create( + environment=environment, application_role=application_role2 + ) + + environment_data = filter_env_roles_form_data(application_role, [environment]) + assert environment_data[0]["role"] != "No Access" + + environment_data = filter_env_roles_form_data(application_role3, [environment]) + assert environment_data[0]["role"] == "No Access" From 0ed782f4e8edf926f0751090f0a94e75a182185d Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:47:21 -0400 Subject: [PATCH 04/14] Move app member creation business logic from route --- atst/routes/applications/settings.py | 69 +++++++++++++++------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index 0cb90159..f08f4dd8 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -154,6 +154,41 @@ def send_application_invitation(invitee_email, inviter_name, token): ) +def handle_create_member(application_id, http_request): + application = Applications.get(application_id) + form = NewMemberForm(http_request.form) + + if form.validate(): + try: + invite = Applications.invite( + application=application, + inviter=g.current_user, + user_data=form.user_data.data, + permission_sets_names=form.data["permission_sets"], + environment_roles_data=form.environment_roles.data, + ) + + send_application_invitation( + invitee_email=invite.email, + inviter_name=g.current_user.full_name, + token=invite.token, + ) + + flash( + "new_application_member", + user_name=invite.user_name, + application_name=application.name, + ) + + except AlreadyExistsError: + return render_template( + "error.html", message="There was an error processing your request." + ) + else: + pass + # TODO: flash error message + + @applications_bp.route("/applications//settings") @user_can(Permissions.VIEW_APPLICATION, message="view application edit form") def settings(application_id): @@ -284,39 +319,7 @@ def delete_environment(environment_id): Permissions.CREATE_APPLICATION_MEMBER, message="create new application member" ) def create_member(application_id): - application = Applications.get(application_id) - form = NewMemberForm(http_request.form) - - if form.validate(): - try: - invite = Applications.invite( - application=application, - inviter=g.current_user, - user_data=form.user_data.data, - permission_sets_names=form.data["permission_sets"], - environment_roles_data=form.environment_roles.data, - ) - - send_application_invitation( - invitee_email=invite.email, - inviter_name=g.current_user.full_name, - token=invite.token, - ) - - flash( - "new_application_member", - user_name=invite.user_name, - application_name=application.name, - ) - - except AlreadyExistsError: - return render_template( - "error.html", message="There was an error processing your request." - ) - else: - pass - # TODO: flash error message - + handle_create_member(application_id, http_request) return redirect( url_for( "applications.settings", From e0e6c0569c959dfee1e20c24102fe7a6232868d3 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:50:11 -0400 Subject: [PATCH 05/14] Add "action" param to members macro --- templates/applications/settings.html | 1 + .../portfolios/applications/fragments/members.html | 13 +++++++------ templates/portfolios/applications/new/step_3.html | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/templates/applications/settings.html b/templates/applications/settings.html index dcf87520..da8f65ca 100644 --- a/templates/applications/settings.html +++ b/templates/applications/settings.html @@ -78,6 +78,7 @@ application, members, new_member_form, + "applications.settings", user_can(permissions.CREATE_APPLICATION_MEMBER)) }}
diff --git a/templates/portfolios/applications/fragments/members.html b/templates/portfolios/applications/fragments/members.html index 9b968693..9380d9d2 100644 --- a/templates/portfolios/applications/fragments/members.html +++ b/templates/portfolios/applications/fragments/members.html @@ -9,10 +9,14 @@ {% macro MemberManagementTemplate( application, members, - new_member_form, + new_member_form, + action, user_can_create_app_member=False ) %} + + {% include "fragments/flash.html" %} + {% if not application.members %}

{{ ("portfolios.applications.team_settings.blank_slate.title" | translate) }}

@@ -31,7 +35,7 @@ {{ MultiStepModalForm( name=new_member_modal_name, form=new_member_form, - form_action=url_for("applications.create_member", application_id=application.id), + form_action=url_for(action, application_id=application.id), steps=[ member_steps.MemberStepOne(new_member_form), member_steps.MemberStepTwo(new_member_form, application) @@ -55,9 +59,6 @@
- {% if g.matchesPath("application-members") %} - {% include "fragments/flash.html" %} - {% endif %} {% for member in members %} {% set modal_name = "edit_member-{}".format(loop.index) %} {% call Modal(modal_name) %} @@ -129,7 +130,7 @@ {{ MultiStepModalForm( name=new_member_modal_name, form=new_member_form, - form_action=url_for("applications.create_member", application_id=application.id), + form_action=url_for(action, application_id=application.id), steps=[ member_steps.MemberStepOne(new_member_form), member_steps.MemberStepTwo(new_member_form, application) diff --git a/templates/portfolios/applications/new/step_3.html b/templates/portfolios/applications/new/step_3.html index 54878b08..6d255831 100644 --- a/templates/portfolios/applications/new/step_3.html +++ b/templates/portfolios/applications/new/step_3.html @@ -8,5 +8,6 @@ application, members, new_member_form, + "applications.update_new_application_step_3", user_can(permissions.CREATE_APPLICATION_MEMBER)) }} {% endblock %} From dfcdee391048e82b2074cbe741a537aa539e39ba Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:52:36 -0400 Subject: [PATCH 06/14] Add step 3 POST route for app member creation --- atst/routes/applications/new.py | 19 ++++++++++++++++++- atst/routes/applications/settings.py | 6 +++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index d3911b84..af5a59f1 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -7,7 +7,11 @@ from atst.forms.application import NameAndDescriptionForm, EnvironmentsForm from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.models.permissions import Permissions from atst.utils.flash import formatted_flash as flash -from atst.routes.applications.settings import get_members_data, get_new_member_form +from atst.routes.applications.settings import ( + get_members_data, + get_new_member_form, + handle_create_member, +) def get_new_application_form(form_data, form_class, application_id=None): @@ -147,3 +151,16 @@ def view_new_application_step_3(application_id): members=members, new_member_form=new_member_form, ) + + +@applications_bp.route("/applications//step_3", methods=["POST"]) +@user_can(Permissions.CREATE_APPLICATION, message="view create new application form") +def update_new_application_step_3(application_id): + + handle_create_member(application_id, http_request) + + return redirect( + url_for( + "applications.view_new_application_step_3", application_id=application_id + ) + ) diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index f08f4dd8..29efb1de 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -154,9 +154,9 @@ def send_application_invitation(invitee_email, inviter_name, token): ) -def handle_create_member(application_id, http_request): +def handle_create_member(application_id, form_data): application = Applications.get(application_id) - form = NewMemberForm(http_request.form) + form = NewMemberForm(form_data) if form.validate(): try: @@ -319,7 +319,7 @@ def delete_environment(environment_id): Permissions.CREATE_APPLICATION_MEMBER, message="create new application member" ) def create_member(application_id): - handle_create_member(application_id, http_request) + handle_create_member(application_id, http_request.form) return redirect( url_for( "applications.settings", From 98b950dfa316ba9b5bfeb599d04dc97bd32b10ee Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:55:41 -0400 Subject: [PATCH 07/14] don't use render template helper function for envs --- atst/routes/applications/new.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index af5a59f1..5d21275a 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -98,12 +98,19 @@ def create_or_update_new_application_step_1(portfolio_id, application_id=None): ) @user_can(Permissions.CREATE_APPLICATION, message="view create new application form") def view_new_application_step_2(portfolio_id, application_id): - return render_new_application_form( - "applications/new/step_2.html", - EnvironmentsForm, - portfolio_id=portfolio_id, - application_id=application_id, - ) + application = Applications.get(application_id) + render_args = { + "form": EnvironmentsForm( + data={ + "environment_names": [ + environment.name for environment in application.environments + ] + } + ), + "application": application, + } + + return render_template("portfolios/applications/new/step_2.html", **render_args) @applications_bp.route( @@ -120,8 +127,8 @@ def update_new_application_step_2(portfolio_id, application_id): flash("application_created", application_name=application.name) return redirect( url_for( - "applications.portfolio_applications", - portfolio_id=application.portfolio_id, + "applications.update_new_application_step_3", + application_id=application_id, ) ) else: From 8df0bfeddef9ed4ff6eed4f86486f3f3e97e10b5 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:56:26 -0400 Subject: [PATCH 08/14] Add app to render_args of render_template helper --- atst/routes/applications/new.py | 1 + 1 file changed, 1 insertion(+) diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index 5d21275a..4eee9ef8 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -29,6 +29,7 @@ def render_new_application_form( if application_id: application = Applications.get(application_id) render_args["form"] = form or form_class(obj=application) + render_args["application"] = application else: render_args["form"] = form or form_class() From 02a4f9284087f8461930e2f577dbc3052690e828 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:57:28 -0400 Subject: [PATCH 09/14] Clean up templates - Make sure heading includes app name - Remove "back to applications" link - Add "submit" link button to step 3 --- templates/applications/new/step_1.html | 10 ++++++---- templates/applications/new/step_2.html | 9 ++++++--- templates/portfolios/applications/new/step_3.html | 13 +++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/templates/applications/new/step_1.html b/templates/applications/new/step_1.html index e5d95922..4c2e0fa3 100644 --- a/templates/applications/new/step_1.html +++ b/templates/applications/new/step_1.html @@ -12,13 +12,15 @@ {% set action = url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id, application_id=application_id) %} {% endif %} - +{% block portfolio_header %} + {% include "portfolios/header.html" %} + {{ StickyCTA(text="Name and Describe New Application") }} +{% endblock %} + {% block application_content %} {% include "fragments/flash.html" %} -
{{ 'portfolios.applications.settings_heading' | translate }}
-
@@ -42,7 +44,7 @@ {% block next_button %} - {{ SaveButton(text=('portfolios.applications.next_button_text' | translate)) }} + {{ SaveButton(text=('portfolios.applications.new.step_1_button_text' | translate)) }} {% endblock %} diff --git a/templates/applications/new/step_2.html b/templates/applications/new/step_2.html index a737c349..fa0954e7 100644 --- a/templates/applications/new/step_2.html +++ b/templates/applications/new/step_2.html @@ -7,14 +7,17 @@ {% set secondary_breadcrumb = 'portfolios.applications.new_application_title' | translate %} +{% block portfolio_header %} + {{ StickyCTA(text=application.name) }} +{% endblock %} + {% block application_content %} {% set modalName = "newApplicationConfirmation" %} {% include "fragments/flash.html" %} -
{{ 'portfolios.applications.settings_heading' | translate }}
-
+
{{ form.csrf_token }} @@ -63,7 +66,7 @@ {% block next_button %} - {{ SaveButton(text=('portfolios.applications.create_button_text' | translate)) }} + {{ SaveButton(text=('portfolios.applications.new.step_2_button_text' | translate)) }} {% endblock %} diff --git a/templates/portfolios/applications/new/step_3.html b/templates/portfolios/applications/new/step_3.html index 6d255831..ac55d077 100644 --- a/templates/portfolios/applications/new/step_3.html +++ b/templates/portfolios/applications/new/step_3.html @@ -1,8 +1,14 @@ + {% extends "portfolios/applications/base.html" %} {% from "portfolios/applications/fragments/members.html" import MemberManagementTemplate %} {% set secondary_breadcrumb = 'portfolios.applications.new_application_title' | translate %} +{% block portfolio_header %} + {% include "portfolios/header.html" %} + {{ StickyCTA(text=application.name) }} +{% endblock %} + {% block application_content %} {{ MemberManagementTemplate( application, @@ -10,4 +16,11 @@ new_member_form, "applications.update_new_application_step_3", user_can(permissions.CREATE_APPLICATION_MEMBER)) }} + + + + Return to Application Settings + + {% endblock %} + From 00547a9b34947fc8f4bea6390b2fd3d470e85651 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 11:57:39 -0400 Subject: [PATCH 10/14] Update translations for app creation buttons --- translations.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/translations.yaml b/translations.yaml index aff4ad76..509dc8c7 100644 --- a/translations.yaml +++ b/translations.yaml @@ -302,8 +302,10 @@ portfolios: add_member: Add a New Team Member add_another_environment: Add another environment app_settings_text: App settings - create_button_text: Create - next_button_text: "Next: Environments" + new: + step_1_button_text: "Save and Add Environments" + step_2_button_text: "Save and Add Members" + step_3_button_text: Save Application create_new_env: Create a new environment. create_new_env_info: Creating an environment gives you access to the Cloud Service Provider. This environment will function within the constraints of the task order, and any costs will be billed against the portfolio. csp_console_text: CSP console From 337c98f8cd25ced437d810d782b45410a4a1b66d Mon Sep 17 00:00:00 2001 From: graham-dds Date: Tue, 1 Oct 2019 09:17:38 -0400 Subject: [PATCH 11/14] Add / update tests - add tests for step3 of new application --- tests/routes/applications/test_new.py | 69 +++++++++++++++++++++- tests/routes/applications/test_settings.py | 1 + 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/tests/routes/applications/test_new.py b/tests/routes/applications/test_new.py index 22c86969..c39f48d5 100644 --- a/tests/routes/applications/test_new.py +++ b/tests/routes/applications/test_new.py @@ -1,7 +1,9 @@ from flask import url_for -from tests.factories import PortfolioFactory, ApplicationFactory -from atst.domain.applications import Applications +from tests.factories import PortfolioFactory, ApplicationFactory, UserFactory +from unittest.mock import Mock +from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS +from atst.models.application_invitation import ApplicationInvitation def test_get_name_and_description_form(client, user_session): @@ -94,3 +96,66 @@ def test_post_environments(client, session, user_session): assert response.status_code == 302 session.refresh(application) assert len(application.environments) == 3 + + +def test_get_members(client, session, user_session): + application = ApplicationFactory.create() + user_session(application.portfolio.owner) + response = client.get( + url_for( + "applications.view_new_application_step_3", application_id=application.id + ) + ) + assert response.status_code == 200 + + +def test_post_member(monkeypatch, client, user_session, session): + job_mock = Mock() + monkeypatch.setattr("atst.jobs.send_mail.delay", job_mock) + user = UserFactory.create() + application = ApplicationFactory.create( + environments=[{"name": "Naboo"}, {"name": "Endor"}] + ) + (env, env_1) = application.environments + + user_session(application.portfolio.owner) + + response = client.post( + url_for("applications.create_member", application_id=application.id), + data={ + "user_data-first_name": user.first_name, + "user_data-last_name": user.last_name, + "user_data-dod_id": user.dod_id, + "user_data-email": user.email, + "environment_roles-0-environment_id": env.id, + "environment_roles-0-role": "Basic Access", + "environment_roles-0-environment_name": env.name, + "environment_roles-1-environment_id": env_1.id, + "environment_roles-1-role": NO_ACCESS, + "environment_roles-1-environment_name": env_1.name, + "perms_env_mgmt": True, + "perms_team_mgmt": True, + "perms_del_env": True, + }, + ) + + assert response.status_code == 302 + expected_url = url_for( + "applications.settings", + application_id=application.id, + fragment="application-members", + _anchor="application-members", + _external=True, + ) + assert response.location == expected_url + assert len(application.roles) == 1 + environment_roles = application.roles[0].environment_roles + assert len(environment_roles) == 1 + assert environment_roles[0].environment == env + + invitation = ( + session.query(ApplicationInvitation).filter_by(dod_id=user.dod_id).one() + ) + assert invitation.role.application == application + + assert job_mock.called diff --git a/tests/routes/applications/test_settings.py b/tests/routes/applications/test_settings.py index ffe6f7a1..c0b60694 100644 --- a/tests/routes/applications/test_settings.py +++ b/tests/routes/applications/test_settings.py @@ -561,6 +561,7 @@ def test_revoke_invite(client, user_session): assert invite.is_revoked assert app_role.status == ApplicationRoleStatus.DISABLED + def test_filter_environment_roles(): application_role = ApplicationRoleFactory.create(user=None) application_role2 = ApplicationRoleFactory.create( From d4bc051eb44b374b62588aa5fda213b8fb03cad8 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Fri, 27 Sep 2019 15:19:16 -0400 Subject: [PATCH 12/14] Use correct flash for step 2 --- atst/routes/applications/new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index 4eee9ef8..413e0d4e 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -125,7 +125,7 @@ def update_new_application_step_2(portfolio_id, application_id): if form.validate(): application = Applications.get(application_id) application = Applications.update(application, form.data) - flash("application_created", application_name=application.name) + flash("application_environments_updated") return redirect( url_for( "applications.update_new_application_step_3", From 94ccda9d849caadf668f8f0eb375f249dbf8708a Mon Sep 17 00:00:00 2001 From: graham-dds Date: Mon, 30 Sep 2019 09:51:51 -0400 Subject: [PATCH 13/14] update .secrets.baseline --- .secrets.baseline | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 3acc6dd6..26e8796a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2019-09-26T13:53:31Z", + "generated_at": "2019-09-30T13:51:34Z", "plugins_used": [ { "base64_limit": 4.5, @@ -199,5 +199,5 @@ } ] }, - "version": "0.12.6" + "version": "0.12.5" } From d4b5e484ee5fa51283f5ecde2477a569b732d540 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Mon, 30 Sep 2019 10:20:24 -0400 Subject: [PATCH 14/14] rearrange templates to follow new dir structure --- atst/routes/applications/new.py | 6 +++--- templates/{portfolios => }/applications/new/step_3.html | 4 ++-- templates/applications/settings.html | 2 +- .../{portfolios/applications => }/fragments/members.html | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) rename templates/{portfolios => }/applications/new/step_3.html (82%) rename templates/{portfolios/applications => }/fragments/members.html (96%) diff --git a/atst/routes/applications/new.py b/atst/routes/applications/new.py index 413e0d4e..fe160d65 100644 --- a/atst/routes/applications/new.py +++ b/atst/routes/applications/new.py @@ -111,7 +111,7 @@ def view_new_application_step_2(portfolio_id, application_id): "application": application, } - return render_template("portfolios/applications/new/step_2.html", **render_args) + return render_template("applications/new/step_2.html", **render_args) @applications_bp.route( @@ -153,7 +153,7 @@ def view_new_application_step_3(application_id): new_member_form = get_new_member_form(application) return render_template( - "portfolios/applications/new/step_3.html", + "applications/new/step_3.html", application_id=application_id, application=application, members=members, @@ -165,7 +165,7 @@ def view_new_application_step_3(application_id): @user_can(Permissions.CREATE_APPLICATION, message="view create new application form") def update_new_application_step_3(application_id): - handle_create_member(application_id, http_request) + handle_create_member(application_id, http_request.form) return redirect( url_for( diff --git a/templates/portfolios/applications/new/step_3.html b/templates/applications/new/step_3.html similarity index 82% rename from templates/portfolios/applications/new/step_3.html rename to templates/applications/new/step_3.html index ac55d077..c963beee 100644 --- a/templates/portfolios/applications/new/step_3.html +++ b/templates/applications/new/step_3.html @@ -1,7 +1,7 @@ -{% extends "portfolios/applications/base.html" %} +{% extends "applications/base.html" %} -{% from "portfolios/applications/fragments/members.html" import MemberManagementTemplate %} +{% from "fragments/members.html" import MemberManagementTemplate %} {% set secondary_breadcrumb = 'portfolios.applications.new_application_title' | translate %} {% block portfolio_header %} diff --git a/templates/applications/settings.html b/templates/applications/settings.html index da8f65ca..e4c51f2c 100644 --- a/templates/applications/settings.html +++ b/templates/applications/settings.html @@ -5,7 +5,7 @@ {% from "components/icon.html" import Icon %} {% import "applications/fragments/new_member_modal_content.html" as member_steps %} {% from "applications/fragments/member_perms_form_fields.html" import MemberPermsFields %} -{% from "portfolios/applications/fragments/members.html" import MemberManagementTemplate %} +{% from "fragments/members.html" import MemberManagementTemplate %} {% from "components/modal.html" import Modal %} {% from "components/multi_step_modal_form.html" import MultiStepModalForm %} {% from "components/pagination.html" import Pagination %} diff --git a/templates/portfolios/applications/fragments/members.html b/templates/fragments/members.html similarity index 96% rename from templates/portfolios/applications/fragments/members.html rename to templates/fragments/members.html index 9380d9d2..31848326 100644 --- a/templates/portfolios/applications/fragments/members.html +++ b/templates/fragments/members.html @@ -1,7 +1,7 @@ {% from "components/alert.html" import Alert %} {% from "components/icon.html" import Icon %} -{% import "fragments/applications/new_member_modal_content.html" as member_steps %} -{% from "fragments/applications/member_perms_form_fields.html" import MemberPermsFields %} +{% import "applications/fragments/new_member_modal_content.html" as member_steps %} +{% from "applications/fragments/member_perms_form_fields.html" import MemberPermsFields %} {% from "components/modal.html" import Modal %} {% from "components/multi_step_modal_form.html" import MultiStepModalForm %} {% from "components/save_button.html" import SaveButton %} @@ -126,7 +126,7 @@
{% if user_can_create_app_member %} - {% import "fragments/applications/new_member_modal_content.html" as member_steps %} + {% import "applications/fragments/new_member_modal_content.html" as member_steps %} {{ MultiStepModalForm( name=new_member_modal_name, form=new_member_form,