Merge pull request #1088 from dod-ccpo/make-application-creation-multistep

Make application creation multistep
This commit is contained in:
graham-dds 2019-09-24 13:56:36 -04:00 committed by GitHub
commit 7e340936dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 419 additions and 134 deletions

View File

@ -3,7 +3,7 @@
"files": "^.secrets.baseline$",
"lines": null
},
"generated_at": "2019-09-20T19:20:43Z",
"generated_at": "2019-09-24T13:51:51Z",
"plugins_used": [
{
"base64_limit": 4.5,
@ -199,5 +199,5 @@
}
]
},
"version": "0.12.6"
"version": "0.12.5"
}

View File

@ -1,4 +1,5 @@
from . import BaseDomainClass
from flask import g
from atst.database import db
from atst.domain.application_roles import ApplicationRoles
from atst.domain.environment_roles import EnvironmentRoles
@ -19,13 +20,14 @@ class Applications(BaseDomainClass):
resource_name = "application"
@classmethod
def create(cls, user, portfolio, name, description, environment_names):
def create(cls, user, portfolio, name, description, environment_names=None):
application = Application(
portfolio=portfolio, name=name, description=description
)
db.session.add(application)
Environments.create_many(user, application, environment_names)
if environment_names:
Environments.create_many(user, application, environment_names)
db.session.commit()
return application
@ -48,7 +50,10 @@ class Applications(BaseDomainClass):
application.name = new_data["name"]
if "description" in new_data:
application.description = new_data["description"]
if "environment_names" in new_data:
Environments.create_many(
g.current_user, application, new_data["environment_names"]
)
db.session.add(application)
db.session.commit()

View File

@ -11,7 +11,7 @@ class EditEnvironmentForm(BaseForm):
)
class ApplicationForm(BaseForm):
class NameAndDescriptionForm(BaseForm):
name = StringField(
label=translate("forms.application.name_label"), validators=[Required()]
)
@ -20,7 +20,7 @@ class ApplicationForm(BaseForm):
)
class NewApplicationForm(ApplicationForm):
class EnvironmentsForm(BaseForm):
environment_names = FieldList(
StringField(label=translate("forms.application.environment_names_label")),
validators=[

View File

@ -3,35 +3,130 @@ from flask import redirect, render_template, request as http_request, url_for, g
from . import applications_bp
from atst.domain.applications import Applications
from atst.domain.portfolios import Portfolios
from atst.forms.application import NewApplicationForm
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
def get_new_application_form(form_data, form_class, application_id=None):
if application_id:
application = Applications.get(application_id)
return form_class(form_data, obj=application)
else:
return form_class(form_data)
def render_new_application_form(
template, form_class, portfolio_id=None, application_id=None, form=None
):
render_args = {"application_id": application_id}
if application_id:
application = Applications.get(application_id)
render_args["form"] = form or form_class(obj=application)
else:
render_args["form"] = form or form_class()
return render_template(template, **render_args)
@applications_bp.route("/portfolios/<portfolio_id>/applications/new")
@applications_bp.route(
"/portfolios/<portfolio_id>/applications/<application_id>/step_1"
)
@user_can(Permissions.CREATE_APPLICATION, message="view create new application form")
def new(portfolio_id):
form = NewApplicationForm()
return render_template("portfolios/applications/new.html", form=form)
def view_new_application_step_1(portfolio_id, application_id=None):
return render_new_application_form(
"portfolios/applications/new/step_1.html",
NameAndDescriptionForm,
portfolio_id=portfolio_id,
application_id=application_id,
)
@applications_bp.route("/portfolios/<portfolio_id>/applications", methods=["POST"])
@user_can(Permissions.CREATE_APPLICATION, message="create new application")
def create(portfolio_id):
@applications_bp.route(
"/portfolios/<portfolio_id>/applications/new",
endpoint="create_new_application_step_1",
methods=["POST"],
)
@applications_bp.route(
"/portfolios/<portfolio_id>/applications/<application_id>/step_1",
endpoint="update_new_application_step_1",
methods=["POST"],
)
@user_can(Permissions.CREATE_APPLICATION, message="view create new application form")
def create_or_update_new_application_step_1(portfolio_id, application_id=None):
portfolio = Portfolios.get_for_update(portfolio_id)
form = NewApplicationForm(http_request.form)
form = get_new_application_form(
{**http_request.form}, NameAndDescriptionForm, application_id
)
if form.validate():
application_data = form.data
Applications.create(
g.current_user,
portfolio,
application_data["name"],
application_data["description"],
application_data["environment_names"],
)
application = None
if application_id:
application = Applications.get(application_id)
application = Applications.update(application, form.data)
else:
application = Applications.create(g.current_user, portfolio, **form.data)
return redirect(
url_for("applications.portfolio_applications", portfolio_id=portfolio_id)
url_for(
"applications.update_new_application_step_2",
portfolio_id=portfolio_id,
application_id=application.id,
)
)
else:
return render_template("portfolios/applications/new.html", form=form)
return (
render_new_application_form(
"portfolios/applications/new/step_1.html",
NameAndDescriptionForm,
portfolio_id,
application_id,
form,
),
400,
)
@applications_bp.route(
"/portfolios/<portfolio_id>/applications/<application_id>/step_2"
)
@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(
"portfolios/applications/new/step_2.html",
EnvironmentsForm,
portfolio_id=portfolio_id,
application_id=application_id,
)
@applications_bp.route(
"/portfolios/<portfolio_id>/applications/<application_id>/step_2", methods=["POST"]
)
@user_can(Permissions.CREATE_APPLICATION, message="view create new application form")
def update_new_application_step_2(portfolio_id, application_id):
form = get_new_application_form(
{**http_request.form}, EnvironmentsForm, application_id
)
if form.validate():
application = Applications.get(application_id)
application = Applications.update(application, form.data)
flash("application_created", application_name=application.name)
return redirect(
url_for(
"applications.portfolio_applications",
portfolio_id=application.portfolio_id,
)
)
else:
return (
render_new_application_form(
"portfolios/applications/new/step_2.html",
EnvironmentsForm,
portfolio_id,
application_id,
form,
),
400,
)

View File

@ -8,8 +8,8 @@ from atst.domain.application_roles import ApplicationRoles
from atst.domain.audit_log import AuditLog
from atst.domain.common import Paginator
from atst.domain.environment_roles import EnvironmentRoles
from atst.forms.application import ApplicationForm, EditEnvironmentForm
from atst.forms.application_member import NewForm as NewMemberForm, UpdateMemberForm
from atst.forms.application import NameAndDescriptionForm, EditEnvironmentForm
from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS
from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.models.permissions import Permissions
@ -125,7 +125,7 @@ def render_settings_page(application, **kwargs):
members = get_members_data(application)
if "application_form" not in kwargs:
kwargs["application_form"] = ApplicationForm(
kwargs["application_form"] = NameAndDescriptionForm(
name=application.name, description=application.description
)
@ -229,7 +229,7 @@ def new_environment(application_id):
@user_can(Permissions.EDIT_APPLICATION, message="update application")
def update(application_id):
application = Applications.get(application_id)
form = ApplicationForm(http_request.form)
form = NameAndDescriptionForm(http_request.form)
if form.validate():
application_data = form.data
Applications.update(application, application_data)

View File

@ -7,6 +7,13 @@ MESSAGES = {
"message_template": "Portfolio '{{portfolio_name}}' has been deleted",
"category": "success",
},
"application_created": {
"title_template": translate("flash.success"),
"message_template": """
{{ "flash.application.created" | translate({"application_name": application_name}) }}
""",
"category": "success",
},
"application_deleted": {
"title_template": translate("flash.success"),
"message_template": """

View File

@ -1,11 +1,11 @@
import FormMixin from '../../mixins/form'
import textinput from '../text_input'
import FormMixin from '../../../mixins/form'
import textinput from '../../text_input'
import * as R from 'ramda'
const createEnvironment = name => ({ name })
export default {
name: 'new-application',
name: 'application-environments',
mixins: [FormMixin],
@ -18,7 +18,6 @@ export default {
type: Object,
default: () => ({}),
},
modalName: String,
},
data: function() {
@ -47,16 +46,18 @@ export default {
errors: [],
environments,
name,
readyToSubmit: false,
changed: true,
}
},
mounted: function() {
this.$root.$on('onEnvironmentAdded', this.addEnvironment)
this.validate()
},
methods: {
addEnvironment: function(event) {
addEnvironment: function(_event) {
this.changed = false
this.environments.push(createEnvironment(''))
},
@ -64,6 +65,7 @@ export default {
if (this.environments.length > 1) {
this.environments.splice(index, 1)
}
this.validate()
},
validate: function() {
@ -75,6 +77,7 @@ export default {
R.filter(Boolean),
R.take(1)
)(this.validations)
this.invalid = this.errors.length > 0
},
hasEnvironments: function() {
@ -99,34 +102,9 @@ export default {
return names.every((n, index) => names.indexOf(n) === index)
},
handleSubmit: function(event) {
if (!this.readyToSubmit) {
event.preventDefault()
this.validateAndOpenModal()
}
},
handleCancelSubmit: function() {
this.readyToSubmit = false
this.closeModal(this.modalName)
},
validateAndOpenModal: function() {
let isValid = this.$children.reduce((previous, newVal) => {
// display textInput error if it is not valid
if (!newVal.showValid) {
newVal.showError = true
}
return newVal.showValid && previous
}, true)
onInput: function(e) {
this.changed = true
this.validate()
isValid = this.errors.length == 0 && isValid
if (isValid) {
this.readyToSubmit = true
this.openModal(this.modalName)
}
},
},
}

View File

@ -0,0 +1,19 @@
import FormMixin from '../../../mixins/form'
import textinput from '../../text_input'
import * as R from 'ramda'
export default {
name: 'application-name-and-description',
mixins: [FormMixin],
components: {
textinput,
},
data: function() {
return {
errors: [],
}
},
}

View File

@ -17,7 +17,8 @@ import EditOfficerForm from './components/forms/edit_officer_form'
import poc from './components/forms/poc'
import oversight from './components/forms/oversight'
import toggler from './components/toggler'
import NewApplication from './components/forms/new_application'
import ApplicationNameAndDescription from './components/forms/new_application/name_and_description'
import ApplicationEnvironments from './components/forms/new_application/environments'
import { EditEnvironmentRole } from './components/forms/edit_environment_role'
import EditApplicationRoles from './components/forms/edit_application_roles'
import MultiStepModalForm from './components/forms/multi_step_modal_form'
@ -60,7 +61,8 @@ const app = new Vue({
checkboxinput,
poc,
oversight,
NewApplication,
ApplicationNameAndDescription,
ApplicationEnvironments,
selector,
BudgetChart,
SpendTable,

View File

@ -13,7 +13,7 @@
<div class='portfolio-applications__header--title col col--grow'>Applications</div>
<div class='portfolio-applications__header--actions col'>
{% if can_create_applications %}
<a class='icon-link' href='{{ url_for('applications.new', portfolio_id=portfolio.id) }}'>
<a class='icon-link' href='{{ url_for('applications.view_new_application_step_1', portfolio_id=portfolio.id) }}'>
{{ 'portfolios.applications.add_application_text' | translate }}
{{ Icon("plus", classes="sidenav__link-icon") }}
</a>
@ -26,7 +26,7 @@
{{ EmptyState(
'This portfolio doesnt have any applications',
action_label='Add a new application' if can_create_applications else None,
action_href=url_for('applications.new', portfolio_id=portfolio.id) if can_create_applications else None,
action_href=url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id) if can_create_applications else None,
icon='cloud',
sub_message=None if can_create_applications else 'Please contact your JEDI Cloud portfolio administrator to set up a new application.',
add_perms=can_create_applications

View File

@ -0,0 +1,51 @@
{% extends "portfolios/applications/base.html" %}
{% from "components/alert.html" import Alert %}
{% from "components/text_input.html" import TextInput %}
{% from 'components/save_button.html' import SaveButton %}
{% set secondary_breadcrumb = 'portfolios.applications.new_application_title' | translate %}
{% if application_id %}
{% set action = url_for('applications.update_new_application_step_1', portfolio_id=portfolio.id, application_id=application_id) %}
{% else %}
{% set action = url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id, application_id=application_id) %}
{% endif %}
{% block application_content %}
{% include "fragments/flash.html" %}
<div class='subheading'>{{ 'portfolios.applications.settings_heading' | translate }}</div>
<application-name-and-description inline-template v-bind:initial-data='{{ form.data|tojson }}'>
<form method="POST" action="{{ action }}" v-on:submit="handleSubmit">
<div class="panel">
<div class="panel__content">
{{ form.csrf_token }}
<p>
{{ "fragments.edit_application_form.explain" | translate }}
</p>
<div class="form-row">
<div class="form-col form-col--two-thirds">
{{ TextInput(form.name, optional=False) }}
</div>
</div>
<div class="form-row">
<div class="form-col form-col--two-thirds">
{{ TextInput(form.description, paragraph=True, optional=False) }}
</div>
</div>
</div>
</div>
<span class="action-group">
{% block next_button %}
{{ SaveButton(text=('portfolios.applications.next_button_text' | translate)) }}
{% endblock %}
</span>
</form>
</application-name-and-description>
{% endblock %}

View File

@ -3,7 +3,6 @@
{% from "components/alert.html" import Alert %}
{% from "components/icon.html" import Icon %}
{% from "components/modal.html" import Modal %}
{% from "components/text_input.html" import TextInput %}
{% from 'components/save_button.html' import SaveButton %}
{% set secondary_breadcrumb = 'portfolios.applications.new_application_title' | translate %}
@ -14,50 +13,11 @@
{% include "fragments/flash.html" %}
<div class='subheading'>{{ 'portfolios.applications.settings_heading' | translate }}</div>
<new-application inline-template v-bind:initial-data='{{ form.data|tojson }}' modal-name='{{ modalName }}'>
<form method="POST" action="{{ url_for('applications.create', portfolio_id=portfolio.id) }}" v-on:submit="handleSubmit">
<application-environments inline-template v-bind:initial-data='{{ form.data|tojson }}'>
<form method="POST" action="{{ url_for('applications.view_new_application_step_2', portfolio_id=portfolio.id, application_id=application_id) }}" v-on:submit="handleSubmit">
<div class="panel">
<div class="panel__content">
{% call Modal(name=modalName, dismissable=False) %}
<h1>Create application !{ name }</h1>
<p>
When you click <em>{{ 'portfolios.applications.create_button_text' | translate }}</em>, the environments
<span v-for="(environment, index) in environments">
<strong>!{environment.name}</strong><template v-if="index < (environments.length - 1)">, </template>
</span>
will be created as individual cloud resource groups under <strong>!{ name }</strong> application.
</p>
<div class='action-group'>
<button autofocus type='submit' class='action-group__action usa-button' tabindex='0'>{{ 'portfolios.applications.create_button_text' | translate }}</button>
<button type='button' v-on:click="handleCancelSubmit" class='icon-link action-group__action' tabindex='0'>Cancel</button>
</div>
{% endcall %}
{{ form.csrf_token }}
<p>
{{ "fragments.edit_application_form.explain" | translate }}
</p>
<div class="form-row">
<div class="form-col form-col--two-thirds">
{{ TextInput(form.name, optional=False) }}
</div>
<div class="form-col form-col--third">
&nbsp;
</div>
</div>
<div class="form-row">
<div class="form-col form-col--two-thirds">
{{ TextInput(form.description, paragraph=True, optional=False) }}
</div>
<div class="form-col form-col--third">
&nbsp;
</div>
</div>
<div> {# this extra div prevents this bug: https://www.pivotaltracker.com/story/show/160768940 #}
<div v-cloak v-for="title in errors" :key="title">
{{ Alert(message=None, level="error", vue_template=True) }}
@ -76,7 +36,7 @@
<li v-for="(environment, i) in environments" class="application-edit__env-list-item">
<div class="usa-input">
<label :for="'environment_names-' + i">Environment Name</label>
<input type="text" :id="'environment_names-' + i" v-model="environment.name" placeholder="e.g. Development, Staging, Production"/> <input type="hidden" :name="'environment_names-' + i" v-model="environment.name"/>
<input type="text" :id="'environment_names-' + i" v-model="environment.name" @input="onInput" placeholder="e.g. Development, Staging, Production"/> <input type="hidden" :name="'environment_names-' + i" v-model="environment.name"/>
</div>
<div class="application-edit__env-list-item-block">
<button v-on:click="removeEnvironment(i)" v-if="environments.length > 1" type="button" class="application-edit__env-list-item__remover">
@ -98,14 +58,15 @@
</button>
</div>
</div>
</div>
</div>
<div class="action-group">
{{ SaveButton(text=('portfolios.applications.create_button_text' | translate)) }}
</div>
<span class="action-group">
{% block next_button %}
{{ SaveButton(text=('portfolios.applications.create_button_text' | translate)) }}
{% endblock %}
</span>
</form>
</new-application>
</application-environments>
{% endblock %}

View File

@ -141,7 +141,7 @@
{{ EmptyState(
'Nothing to report',
action_label='Add a new application' if can_create_applications else None,
action_href=url_for('applications.new', portfolio_id=portfolio.id) if can_create_applications else None,
action_href=url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id) if can_create_applications else None,
icon='chart',
sub_message=message,
add_perms=can_create_applications

View File

@ -44,7 +44,7 @@ def test_user_with_permission_has_add_application_link(client, user_session):
url_for("applications.portfolio_applications", portfolio_id=portfolio.id)
)
assert (
url_for("applications.create", portfolio_id=portfolio.id)
url_for("applications.create_new_application_step_1", portfolio_id=portfolio.id)
in response.data.decode()
)
@ -58,7 +58,7 @@ def test_user_without_permission_has_no_add_application_link(client, user_sessio
url_for("applications.portfolio_applications", portfolio_id=portfolio.id)
)
assert (
url_for("applications.create", portfolio_id=portfolio.id)
url_for("applications.create_new_application_step_1", portfolio_id=portfolio.id)
not in response.data.decode()
)

View File

@ -1,21 +1,96 @@
from flask import url_for
from tests.factories import PortfolioFactory
from tests.factories import PortfolioFactory, ApplicationFactory
from atst.domain.applications import Applications
def test_creating_application(client, user_session):
def test_get_name_and_description_form(client, user_session):
portfolio = PortfolioFactory.create()
user_session(portfolio.owner)
response = client.get(
url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id)
)
assert response.status_code == 200
def test_get_name_and_description_form_for_update(client, user_session):
name = "My Test Application"
description = "This is the description of the test application."
application = ApplicationFactory.create(name=name, description=description)
user_session(application.portfolio.owner)
response = client.get(
url_for(
"applications.view_new_application_step_1",
portfolio_id=application.portfolio.id,
application_id=application.id,
)
)
assert response.status_code == 200
assert name in response.data.decode()
assert description in response.data.decode()
def test_post_name_and_description(client, user_session):
portfolio = PortfolioFactory.create()
user_session(portfolio.owner)
response = client.post(
url_for("applications.create", portfolio_id=portfolio.id),
data={
"name": "Test Application",
"description": "This is only a test",
"environment_names-0": "dev",
"environment_names-1": "staging",
"environment_names-2": "prod",
},
url_for(
"applications.create_new_application_step_1", portfolio_id=portfolio.id
),
data={"name": "Test Application", "description": "This is only a test"},
)
assert response.status_code == 302
assert len(portfolio.applications) == 1
assert len(portfolio.applications[0].environments) == 3
assert portfolio.applications[0].name == "Test Application"
assert portfolio.applications[0].description == "This is only a test"
def test_post_name_and_description_for_update(client, session, user_session):
application = ApplicationFactory.create()
user_session(application.portfolio.owner)
response = client.post(
url_for(
"applications.update_new_application_step_1",
portfolio_id=application.portfolio.id,
application_id=application.id,
),
data={"name": "Test Application", "description": "This is only a test"},
)
assert response.status_code == 302
session.refresh(application)
assert application.name == "Test Application"
assert application.description == "This is only a test"
def test_get_environments(client, user_session):
application = ApplicationFactory.create()
user_session(application.portfolio.owner)
response = client.get(
url_for(
"applications.view_new_application_step_2",
portfolio_id=application.portfolio.id,
application_id=application.id,
)
)
assert response.status_code == 200
def test_post_environments(client, session, user_session):
application = ApplicationFactory.create(environments=[])
user_session(application.portfolio.owner)
response = client.post(
url_for(
"applications.update_new_application_step_2",
portfolio_id=application.portfolio.id,
application_id=application.id,
),
data={
"environment_names-0": "development",
"environment_names-1": "staging",
"environment_names-2": "production",
},
)
assert response.status_code == 302
session.refresh(application)
assert len(application.environments) == 3

View File

@ -222,17 +222,105 @@ def test_applications_access_environment_access(get_url_assert_status):
get_url_assert_status(ccpo, url, 404)
# applications.create
def test_applications_create_access(post_url_assert_status):
# applications.view_new_application_step_1
def test_applications_get_application_step_1(get_url_assert_status):
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
owner = user_with()
rando = user_with()
portfolio = PortfolioFactory.create(owner=owner)
url = url_for("applications.create", portfolio_id=portfolio.id)
post_url_assert_status(ccpo, url, 200)
post_url_assert_status(owner, url, 200)
post_url_assert_status(rando, url, 404)
url = url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id)
get_url_assert_status(ccpo, url, 200)
get_url_assert_status(owner, url, 200)
get_url_assert_status(rando, url, 404)
# applications.view_new_application_step_1
def test_applications_get_application_step_1_update(get_url_assert_status):
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
owner = user_with()
rando = user_with()
portfolio = PortfolioFactory.create(owner=owner)
application = ApplicationFactory.create(portfolio=portfolio)
url = url_for(
"applications.view_new_application_step_1",
portfolio_id=portfolio.id,
application_id=application.id,
)
get_url_assert_status(ccpo, url, 200)
get_url_assert_status(owner, url, 200)
get_url_assert_status(rando, url, 404)
# applications.create_or_update_new_application_step_1
def test_applications_post_application_step_1(post_url_assert_status):
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
owner = user_with()
rando = user_with()
portfolio = PortfolioFactory.create(owner=owner)
application = ApplicationFactory.create(portfolio=portfolio)
step_1_form_data = {
"name": "Test Application",
"description": "This is only a test",
}
url = url_for(
"applications.create_new_application_step_1", portfolio_id=portfolio.id
)
post_url_assert_status(ccpo, url, 302, data=step_1_form_data)
post_url_assert_status(owner, url, 302, data=step_1_form_data)
post_url_assert_status(rando, url, 404, data=step_1_form_data)
url = url_for(
"applications.update_new_application_step_1",
portfolio_id=portfolio.id,
application_id=application.id,
)
post_url_assert_status(ccpo, url, 302, data=step_1_form_data)
post_url_assert_status(owner, url, 302, data=step_1_form_data)
post_url_assert_status(rando, url, 404, data=step_1_form_data)
# applications.view_new_application_step_2
def test_applications_get_application_step_2(get_url_assert_status):
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
owner = user_with()
rando = user_with()
portfolio = PortfolioFactory.create(owner=owner)
application = ApplicationFactory.create(portfolio=portfolio)
url = url_for(
"applications.view_new_application_step_2",
portfolio_id=portfolio.id,
application_id=application.id,
)
get_url_assert_status(ccpo, url, 200)
get_url_assert_status(owner, url, 200)
get_url_assert_status(rando, url, 404)
# applications.update_new_application_step_2
def test_applications_post_application_step_2(post_url_assert_status):
ccpo = user_with(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
owner = user_with()
rando = user_with()
portfolio = PortfolioFactory.create(owner=owner)
application = ApplicationFactory.create(portfolio=portfolio)
step_2_form_data = {
"environment_names-0": "development",
"environment_names-1": "staging",
"environment_names-2": "production",
}
url = url_for(
"applications.update_new_application_step_1",
portfolio_id=portfolio.id,
application_id=application.id,
)
post_url_assert_status(ccpo, url, 302, data=step_2_form_data)
post_url_assert_status(owner, url, 302, data=step_2_form_data)
post_url_assert_status(rando, url, 404, data=step_2_form_data)
# portfolios.invite_member
@ -319,7 +407,9 @@ def test_applications_new_access(get_url_assert_status):
rando = user_with()
portfolio = PortfolioFactory.create(owner=owner)
url = url_for("applications.new", portfolio_id=portfolio.id)
url = url_for(
"applications.create_new_application_step_1", portfolio_id=portfolio.id
)
get_url_assert_status(ccpo, url, 200)
get_url_assert_status(owner, url, 200)
get_url_assert_status(rando, url, 404)

View File

@ -101,6 +101,7 @@ email:
portfolio_invite: "{inviter_name} has invited you to a JEDI cloud portfolio"
flash:
application:
created: 'You have successfully created the {application_name} application.'
deleted: 'You have successfully deleted the {application_name} application. To view the retained activity log, visit the portfolio administration page.'
delete_member_success: 'You have successfully deleted {member_name} from the portfolio.'
deleted_member: Portfolio member deleted
@ -296,6 +297,7 @@ portfolios:
add_another_environment: Add another environment
app_settings_text: App settings
create_button_text: Create
next_button_text: "Next: Environments"
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