Merge pull request #177 from dod-ccpo/designate-technical-poc-159495040
Designate technical poc 159495040
This commit is contained in:
commit
d0af633449
@ -6,3 +6,9 @@ class ValidatedForm(FlaskForm):
|
||||
"""Performs any applicable extra validation. Must
|
||||
return True if the form is valid or False otherwise."""
|
||||
return True
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
_data = super().data
|
||||
_data.pop("csrf_token", None)
|
||||
return _data
|
||||
|
@ -1,11 +1,30 @@
|
||||
from wtforms.fields import StringField
|
||||
from wtforms.fields import StringField, BooleanField
|
||||
from wtforms.fields.html5 import EmailField
|
||||
from wtforms.validators import Required, Email, Length
|
||||
from wtforms.validators import Required, Email, Length, Optional
|
||||
from .forms import ValidatedForm
|
||||
from .validators import IsNumber
|
||||
|
||||
|
||||
class POCForm(ValidatedForm):
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
if self.am_poc.data:
|
||||
# Prepend Optional validators so that the validation chain
|
||||
# halts if no data exists.
|
||||
self.fname_poc.validators.insert(0, Optional())
|
||||
self.lname_poc.validators.insert(0, Optional())
|
||||
self.email_poc.validators.insert(0, Optional())
|
||||
self.dodid_poc.validators.insert(0, Optional())
|
||||
|
||||
return super().validate(*args, **kwargs)
|
||||
|
||||
|
||||
am_poc = BooleanField(
|
||||
"I am the Workspace Owner.",
|
||||
default=False,
|
||||
false_values=(False, "false", "False", "no", "")
|
||||
)
|
||||
|
||||
fname_poc = StringField("First Name", validators=[Required()])
|
||||
|
||||
lname_poc = StringField("Last Name", validators=[Required()])
|
||||
|
@ -65,7 +65,7 @@ class JEDIRequestFlow(object):
|
||||
return {
|
||||
"fname_request": user.first_name,
|
||||
"lname_request": user.last_name,
|
||||
"email_request": user.email
|
||||
"email_request": user.email,
|
||||
}
|
||||
|
||||
@property
|
||||
@ -80,7 +80,7 @@ class JEDIRequestFlow(object):
|
||||
data = self.request.body
|
||||
elif self.form_section == "information_about_you":
|
||||
form_data = self.request.body.get(self.form_section, {})
|
||||
data = { **self.map_user_data(self.request.creator), **form_data }
|
||||
data = {**self.map_user_data(self.request.creator), **form_data}
|
||||
else:
|
||||
data = self.request.body.get(self.form_section, {})
|
||||
elif self.form_section == "information_about_you":
|
||||
@ -103,40 +103,36 @@ class JEDIRequestFlow(object):
|
||||
"title": "Details of Use",
|
||||
"section": "details_of_use",
|
||||
"form": RequestForm,
|
||||
"subitems": [
|
||||
{
|
||||
"title": "Overall request details",
|
||||
"id": "overall-request-details",
|
||||
},
|
||||
{"title": "Cloud Resources", "id": "cloud-resources"},
|
||||
{"title": "Support Staff", "id": "support-staff"},
|
||||
],
|
||||
"show": True,
|
||||
},
|
||||
{
|
||||
"title": "Information About You",
|
||||
"section": "information_about_you",
|
||||
"form": OrgForm,
|
||||
"show": True,
|
||||
},
|
||||
{
|
||||
"title": "Workspace Owner",
|
||||
"section": "primary_poc",
|
||||
"form": POCForm,
|
||||
"show": True,
|
||||
},
|
||||
{"title": "Workspace Owner", "section": "primary_poc", "form": POCForm},
|
||||
{
|
||||
"title": "Review & Submit",
|
||||
"section": "review_submit",
|
||||
"form": ReviewForm,
|
||||
"show": True,
|
||||
},
|
||||
]
|
||||
|
||||
def create_or_update_request(self):
|
||||
request_data = {self.form_section: self.form.data}
|
||||
request_data = self.map_request_data(self.form_section, self.form.data)
|
||||
if self.request_id:
|
||||
Requests.update(self.request_id, request_data)
|
||||
else:
|
||||
request = Requests.create(self.current_user, request_data)
|
||||
self.request_id = request.id
|
||||
|
||||
def map_request_data(self, section, data):
|
||||
if section == "primary_poc":
|
||||
if data.get("am_poc", False):
|
||||
data = {
|
||||
**data,
|
||||
"dodid_poc": self.current_user.dod_id,
|
||||
"fname_poc": self.current_user.first_name,
|
||||
"lname_poc": self.current_user.last_name,
|
||||
"email_poc": self.current_user.email,
|
||||
}
|
||||
return {section: data}
|
||||
|
@ -64,35 +64,30 @@ def requests_update(screen=1, request_id=None):
|
||||
existing_request=existing_request,
|
||||
)
|
||||
|
||||
rerender_args = dict(
|
||||
f=jedi_flow.form,
|
||||
data=post_data,
|
||||
screens=jedi_flow.screens,
|
||||
current=screen,
|
||||
next_screen=jedi_flow.next_screen,
|
||||
request_id=jedi_flow.request_id,
|
||||
)
|
||||
has_next_screen = jedi_flow.next_screen <= len(jedi_flow.screens)
|
||||
valid = jedi_flow.validate() and jedi_flow.validate_warnings()
|
||||
|
||||
if jedi_flow.validate():
|
||||
if valid:
|
||||
jedi_flow.create_or_update_request()
|
||||
valid = jedi_flow.validate_warnings()
|
||||
if valid:
|
||||
if jedi_flow.next_screen > len(jedi_flow.screens):
|
||||
where = "/requests"
|
||||
else:
|
||||
where = url_for(
|
||||
"requests.requests_form_update",
|
||||
screen=jedi_flow.next_screen,
|
||||
request_id=jedi_flow.request_id,
|
||||
)
|
||||
return redirect(where)
|
||||
|
||||
else:
|
||||
return render_template(
|
||||
"requests/screen-%d.html" % int(screen), **rerender_args
|
||||
if has_next_screen:
|
||||
where = url_for(
|
||||
"requests.requests_form_update",
|
||||
screen=jedi_flow.next_screen,
|
||||
request_id=jedi_flow.request_id,
|
||||
)
|
||||
|
||||
else:
|
||||
where = "/requests"
|
||||
return redirect(where)
|
||||
else:
|
||||
rerender_args = dict(
|
||||
f=jedi_flow.form,
|
||||
data=post_data,
|
||||
screens=jedi_flow.screens,
|
||||
current=screen,
|
||||
next_screen=jedi_flow.next_screen,
|
||||
request_id=jedi_flow.request_id,
|
||||
)
|
||||
return render_template("requests/screen-%d.html" % int(screen), **rerender_args)
|
||||
|
||||
|
||||
|
16
js/components/checkbox_input.js
Normal file
16
js/components/checkbox_input.js
Normal file
@ -0,0 +1,16 @@
|
||||
export default {
|
||||
name: 'checkboxinput',
|
||||
|
||||
props: {
|
||||
name: String,
|
||||
},
|
||||
|
||||
methods: {
|
||||
onInput: function (e) {
|
||||
this.$root.$emit('field-change', {
|
||||
value: e.target.checked,
|
||||
name: this.name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
43
js/components/forms/poc.js
Normal file
43
js/components/forms/poc.js
Normal file
@ -0,0 +1,43 @@
|
||||
import optionsinput from '../options_input'
|
||||
import textinput from '../text_input'
|
||||
import checkboxinput from '../checkbox_input'
|
||||
|
||||
export default {
|
||||
name: 'poc',
|
||||
|
||||
components: {
|
||||
optionsinput,
|
||||
textinput,
|
||||
checkboxinput,
|
||||
},
|
||||
|
||||
props: {
|
||||
initialData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
|
||||
data: function () {
|
||||
const {
|
||||
am_poc = false
|
||||
} = this.initialData
|
||||
|
||||
return {
|
||||
am_poc
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
this.$root.$on('field-change', this.handleFieldChange)
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleFieldChange: function (event) {
|
||||
const { value, name } = event
|
||||
if (typeof this[name] !== undefined) {
|
||||
this[name] = value
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
@ -4,7 +4,9 @@ import VTooltip from 'v-tooltip'
|
||||
|
||||
import optionsinput from './components/options_input'
|
||||
import textinput from './components/text_input'
|
||||
import checkboxinput from './components/checkbox_input'
|
||||
import DetailsOfUse from './components/forms/details_of_use'
|
||||
import poc from './components/forms/poc'
|
||||
|
||||
Vue.use(VTooltip)
|
||||
|
||||
@ -14,7 +16,9 @@ const app = new Vue({
|
||||
components: {
|
||||
optionsinput,
|
||||
textinput,
|
||||
checkboxinput,
|
||||
DetailsOfUse,
|
||||
poc,
|
||||
},
|
||||
methods: {
|
||||
closeModal: function(name) {
|
||||
|
18
templates/components/checkbox_input.html
Normal file
18
templates/components/checkbox_input.html
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
{% macro CheckboxInput(field, inline=False) -%}
|
||||
<checkboxinput name='{{ field.name }}' inline-template key='{{ field.name }}'>
|
||||
<div class='usa-input {% if field.errors %}usa-input--error{% endif %}'>
|
||||
|
||||
<fieldset v-on:change="onInput" class="usa-input__choices {% if inline %}usa-input__choices--inline{% endif %}">
|
||||
<legend>
|
||||
{{ field() }}
|
||||
{{ field.label }}
|
||||
|
||||
{% if field.description %}
|
||||
<span class='usa-input__help'>{{ field.description | safe }}</span>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
</checkboxinput>
|
||||
|
||||
{%- endmacro %}
|
@ -2,6 +2,7 @@
|
||||
|
||||
{% from "components/alert.html" import Alert %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
{% from "components/checkbox_input.html" import CheckboxInput %}
|
||||
|
||||
{% block subtitle %}
|
||||
<h2>Designate a Workspace Owner</h2>
|
||||
@ -16,20 +17,31 @@
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
<p>The Workspace Owner is the primary point of contact and technical administrator of the JEDI Workspace and will have the following responsibilities:</p>
|
||||
<ul>
|
||||
<li>Organize your cloud-hosted systems into projects and environments</li>
|
||||
<li>Add users to this workspace and manage members</li>
|
||||
<li>Manage access to the JEDI Cloud service provider’s portal</li>
|
||||
</ul>
|
||||
</p>
|
||||
<poc inline-template v-bind:initial-data='{{ f.data|tojson }}'>
|
||||
<div>
|
||||
|
||||
<p>This person must be a DoD employee (not a contractor).</p>
|
||||
<p>The Workspace Owner may be you. You will be able to add other administrators later. This person will be invited via email once your request is approved.</p>
|
||||
<p>The Workspace Owner is the primary point of contact and technical administrator of the JEDI Workspace and will have the
|
||||
following responsibilities:</p>
|
||||
<ul>
|
||||
<li>Organize your cloud-hosted systems into projects and environments</li>
|
||||
<li>Add users to this workspace and manage members</li>
|
||||
<li>Manage access to the JEDI Cloud service provider’s portal</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
{{ TextInput(f.fname_poc,placeholder='First Name') }}
|
||||
{{ TextInput(f.lname_poc,placeholder='Last Name') }}
|
||||
{{ TextInput(f.email_poc,placeholder='jane@mail.mil', validation='email') }}
|
||||
{{ TextInput(f.dodid_poc,placeholder='10-digit number on the back of the CAC', validation='dodId') }}
|
||||
<p>This person must be a DoD employee (not a contractor).</p>
|
||||
<p>The Workspace Owner may be you. You will be able to add other administrators later. This person will be invited via email
|
||||
once your request is approved.</p>
|
||||
|
||||
{{ CheckboxInput(f.am_poc) }}
|
||||
|
||||
<template v-if="!am_poc" v-cloak>
|
||||
{{ TextInput(f.fname_poc,placeholder='First Name') }}
|
||||
{{ TextInput(f.lname_poc,placeholder='Last Name') }}
|
||||
{{ TextInput(f.email_poc,placeholder='jane@mail.mil', validation='email') }}
|
||||
{{ TextInput(f.dodid_poc,placeholder='10-digit number on the back of the CAC', validation='dodId') }}
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</poc>
|
||||
{% endblock %}
|
||||
|
@ -56,6 +56,7 @@ class RequestFactory(factory.alchemy.SQLAlchemyModelFactory):
|
||||
def build_request_body(cls, user, dollar_value=1000000):
|
||||
return {
|
||||
"primary_poc": {
|
||||
"am_poc": False,
|
||||
"dodid_poc": user.dod_id,
|
||||
"email_poc": user.email,
|
||||
"fname_poc": user.first_name,
|
||||
|
@ -1,9 +1,8 @@
|
||||
import re
|
||||
import pytest
|
||||
import urllib
|
||||
from tests.mocks import MOCK_USER, MOCK_REQUEST
|
||||
from tests.factories import RequestFactory, UserFactory
|
||||
from atst.domain.roles import Roles
|
||||
from atst.domain.requests import Requests
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
||||
ERROR_CLASS = "alert--error"
|
||||
@ -103,6 +102,65 @@ def test_non_creator_info_is_not_autopopulated(monkeypatch, client, user_session
|
||||
assert not user.last_name in body
|
||||
assert not user.email in body
|
||||
|
||||
def test_am_poc_causes_poc_to_be_autopopulated(client, user_session):
|
||||
creator = UserFactory.create()
|
||||
user_session(creator)
|
||||
request = RequestFactory.create(creator=creator, body={})
|
||||
client.post(
|
||||
"/requests/new/3/{}".format(request.id),
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
data="am_poc=yes",
|
||||
)
|
||||
request = Requests.get(request.id)
|
||||
assert request.body["primary_poc"]["dodid_poc"] == creator.dod_id
|
||||
|
||||
|
||||
def test_not_am_poc_requires_poc_info_to_be_completed(client, user_session):
|
||||
creator = UserFactory.create()
|
||||
user_session(creator)
|
||||
request = RequestFactory.create(creator=creator, body={})
|
||||
response = client.post(
|
||||
"/requests/new/3/{}".format(request.id),
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
data="am_poc=no",
|
||||
follow_redirects=True
|
||||
)
|
||||
assert ERROR_CLASS in response.data.decode()
|
||||
|
||||
|
||||
def test_not_am_poc_allows_user_to_fill_in_poc_info(client, user_session):
|
||||
creator = UserFactory.create()
|
||||
user_session(creator)
|
||||
request = RequestFactory.create(creator=creator, body={})
|
||||
poc_input = {
|
||||
"am_poc": "no",
|
||||
"fname_poc": "test",
|
||||
"lname_poc": "user",
|
||||
"email_poc": "test.user@mail.com",
|
||||
"dodid_poc": "1234567890",
|
||||
}
|
||||
response = client.post(
|
||||
"/requests/new/3/{}".format(request.id),
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
data=urlencode(poc_input),
|
||||
)
|
||||
assert ERROR_CLASS not in response.data.decode()
|
||||
|
||||
|
||||
def test_poc_details_can_be_autopopulated_on_new_request(client, user_session):
|
||||
creator = UserFactory.create()
|
||||
user_session(creator)
|
||||
response = client.post(
|
||||
"/requests/new/3",
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||
data="am_poc=yes",
|
||||
)
|
||||
request_id = response.headers["Location"].split('/')[-1]
|
||||
request = Requests.get(request_id)
|
||||
|
||||
assert request.body["primary_poc"]["dodid_poc"] == creator.dod_id
|
||||
|
||||
|
||||
def test_can_review_data(user_session, client):
|
||||
creator = UserFactory.create()
|
||||
user_session(creator)
|
||||
|
Loading…
x
Reference in New Issue
Block a user