Merge pull request #1088 from dod-ccpo/make-application-creation-multistep
Make application creation multistep
This commit is contained in:
commit
7e340936dc
@ -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"
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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=[
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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": """
|
||||
|
@ -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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
19
js/components/forms/new_application/name_and_description.js
Normal file
19
js/components/forms/new_application/name_and_description.js
Normal 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: [],
|
||||
}
|
||||
},
|
||||
}
|
@ -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,
|
||||
|
@ -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 doesn’t 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
|
||||
|
51
templates/portfolios/applications/new/step_1.html
Normal file
51
templates/portfolios/applications/new/step_1.html
Normal 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 %}
|
@ -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">
|
||||
|
||||
</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">
|
||||
|
||||
</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 %}
|
@ -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
|
||||
|
@ -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()
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user