Merge pull request #177 from dod-ccpo/designate-technical-poc-159495040

Designate technical poc 159495040
This commit is contained in:
richard-dds 2018-08-15 11:43:24 -04:00 committed by GitHub
commit d0af633449
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 230 additions and 62 deletions

View File

@ -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

View File

@ -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()])

View File

@ -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}

View File

@ -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)

View 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
})
}
}
}

View 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
}
},
}
}

View File

@ -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) {

View 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 %}

View File

@ -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 providers 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 providers 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 %}

View File

@ -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,

View File

@ -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)